Moving up the corporate ladder no longer relies on the years of experience you have. Sure, it may be a deciding factor in a tiebreaker, but it tells little about what you could bring to the table. What matters more is how you write your code, the quality of the code, and the future possibilities that may arise from re-using the piece of said code. Learning the best conventions employed in the industry is as simple as going through well-known open-source code or reading the one-pagers of the tool you’re using —
More often than not, when we think of designing for scalability we think of writing code that won’t be used until a later date. This is far from the truth! Such code shouldn’t exist in the first place — it creates a tech debt and can lead to maintainability issues down the road if it ends up not being used at all!
The main idea of scalability is to use it from the get-go so that it can be expanded on in future sprints or marathons with minimal effort and little to no changes. To give an example of this, the startup I work for has partnered up with many Payroll and POS companies to provide our customers with seamless third-party integrations within the product. Though each integration is unique, the task that is required is mostly the same.
Let’s say when importing employees from the third-party systems, Gusto and BambooHR, they should have the following setup requirements:
Here, the solution is simple, as shown in the sample Vue.js code below:
<template>
...
<location-mapping :set="setMapping" :integration:"state.integration" v-if="state.index === 0" />
<employee-invite :set="setInvite" v-if="state.index === 1"/>
...
<button :click="nextTab" v-if="state.index !==1 ">Next</button>>
<button :click="startImport" v-else">Start Import</button>
</template>
...
setup() {
const state = reactive({
index: 0,
integration: $route.params.integration,
configuration: {},
});
function startImport() {
startBackgroundJob(state.integration, state.configuration);
}
function setMapping(mapping) {
state.configuration.locationMapping = mapping;
}
function setInvite(canInviteEmployees) {
state.configuration.inviteEmployees = canInviteEmployees;
}
function nextTab() {
state.index++;
}
return { state, startImport, setMapping, setInvite, nextTab };
}
But what if we add another integration, Zero, that also requires the mapping for the leave types (i.e. syncing the annual leave, sick leave, and paternity leave the employee may have, into our system). We could either duplicate the template we have above and add a new element, leave-mapping, or we could reuse this template and ensure the integrations only see the elements that are required by them. To do this, most developers would make use of if statements, which makes a perfect segway into my next coding practice.
Take this for code for example:
if gusto show this:
...
if bamboohr show that:
...
...
While this should do the trick, doing this leads to a code that is unreadable and potentially harder to scale, especially if you’re working with a lot more integrations than just the 3 mentioned above.
Instead, making use of definitions in the form of JavaScript objects is much faster and it makes it easier for the application to scale. Have a look at the following code 👨💻
LOCATION_MAPPING = {
COMPONENT: 'location-mapping',
...otherRequiredProperties,
}
EMPLOYEE_INVITE = {
COMPONENT: 'employee-invite',
...otherRequiredProperties,
}
LEAVE_TYPE_MAPPING = {
COMPONENT: 'leave-type-mapping',
...otherRequiredProperties,
}
INTEGRATIONS = {
BAMBOOHR: [LOCATION_MAPPING, EMPLOYEE_INVITE],
GUSTO: [LOCATION_MAPPING, EMPLOYEE_INVITE],
ZERO: [LOCATION_MAPPING, LEAVE_TYPE_MAPPING, EMPLOYEE_INVITE],
}
We have created an object that holds all the integrations we are working with for this specific task. Each integration has properties of the components that it’ll be making use of, for example, BambooHR makes use of LOCATION_MAPPING and EMPLOYEE_INVITE which in turn provides us with useful information associated with them that allows us to scale. One of this information is COMPONENT which essentially tells us the name of the Vue template related to them. By having the name of the component available to us, we can make use of Vue’s <component :is="componentName" />
Now, our code should look so much cleaner and with minimal changes, we should be able to scale this feature to many integrations as required. Not only does it help with scalability, but it also drastically improves the readability and maintainability of the code.
<template>
...
<component :is="state.currentTab" />
<button :v-if="state.hasNext" @click="nextTab">Next</button>
<button :v-else @click="startImport">Start Import</button>
</template>
// import definitions
// import components
...
setup() {
const state = reactive({
tabIndex: 0,
state.integration: $route.params.integration,
currentTab: computed(() => INTEGRATIONS[state.integration][state.tabIndex].COMPONENT),
hasNext: computed(() => state.tabIndex + 1 !== INTEGRATIONS[state.integration].length
}); function nextTab() {
state.tabIndex++;
} function startImport() {
// Do what is needed
} return { state, nextTab, startImport };
}
There are a lot more reasons why if
statements should be avoided but that’ll be for another article!
Naming things has been a huge problem since the start of time. Yes, you could probably name a variable alien
, wizard
, or have a do_things
function, however, while it’ll be getting the job done at that moment, it’ll also be making it 100x harder for the next person, or even yourself in the future, to understand what the variables and functions are used for. Instead of getting straight into fixing a bug or introducing a new feature, you’ll be spending valuable engineering time backtracking the code to get some context of what’s going on.
One rule I follow when it comes to naming variables and functions is to use words that would be clear for any stakeholder to have a good understanding of what the variables or functions may be for. Carl Alexander has an excellent
Just like the if statements, comments are one of those things that are better left out of the code. Does it help the programmer get a better understanding of the code? Well, it depends. Have a look at the following block of code:
// start count
let count = 0;
Does this comment have information that could not be told by just reading the code? Not at all! Comments as such are disruptive and require the programmer to constantly switch contexts, which comes at a cost. The main rule of thumb is to write code that does not require you to explain what it does in plain English. This also means having a function that does only one job, which goes hand-in-hand with naming things correctly. Having a function named getThirdPartySalesData
should give an idea of what we can expect from this function, and hence comments are not required at all! That being said, understanding what the required parameters for the function are can be tricky, and even more tricky is knowing, in this case, what properties we can expect in the returned object. For cases as such, and for the purposes of documentation, we can use JSDoc, PHPDoc, among others.
JSDoc is a documentation generator for JavaScript that analyses the code and automatically generates a static page with all the documentation. This is great when working with large teams, but what we’re after is its integration with the IDE / Code Editor that makes programming a lot easier through auto-completion suggestions.
Also Published Here