Remember when you were in grade school, and the teacher would have several dates in place for a paper?

09/29 — outline due 10/5 — rough draft due 10/15 — final paper due

Each step in the process allowed you to make advancements towards a finished product without the overload of the project as a whole. Iterative coding is much like this, where it takes a story and breaks it into sizable steps to more easily meet the requirements.

What is Iterative Coding?

Iterative coding is taking defined piece of work and splitting it into phased chunks in order to expedite the creation of completed, clean, tested code. It typically starts with researching and designing an end goal architecture. Rough draft code is then written to get full functionality, which is followed by code refactoring and cleaning. Lastly, unit tests are written to solidify the code so that any future changes or additions are validated before being merged into the codebase.

Iterative coding is taking defined piece of work and splitting it into phased chunks in order to expedite the creation of completed, clean, tested code.

Benefits of Iterative Coding

Speed — One of the best benefits of iterative coding is that things can be done quickly. Without having to worry about the final state of the code until it is time, full functionality can be flushed out. Once functionality is completed, then the code can be quickly refactored to the final state without worrying if everything still works. Clean Code — Since iterative coding is highly focused on the separation of concerns, there is an entire phase of development dedicated refactoring code to a clean state. This involves renaming, splitting up functions and files, hardening space and time complexities, and linting. This step is typically one of the final phases in the process, since cleaning shouldn’t happen while functionality is still being added. Fully Tested — Never forget to write good tests for your code. For most developers, writing tests is the biggest sore spot because testing isn’t as exciting as writing feature based code. Nonetheless, testing code is an important way to make sure people who touch the codebase in the future do not break the intended functionality. A major difference worth pointing out between iterative coding techniques and test driven design (TDD) techniques, is that iterative coding tests are written at the end of the development of a story, whereas TDD forces tests to be written beforehand. Iterative coding is founded on the principle that you do not know the final state of the code before it is written. Thus, testing should wait until after the cleaning phase to harden what you programmed. There is a lot of contention in the programming world about when testing is done (before or after code is written), but the most important thing is that tests are created to strengthen the integrity of the codebase.

Iterative coding is founded on the principle that you do not know the final state of the code before it is written

How to Implement Iterative Coding Practices

Let’s work through a mock story, and see how to successfully implement iterative coding practices. Imagine our product manager comes to the team with this story: Our user’s need a way to refresh 3 months worth of data pulled from an external account.

To start, let’s get full functionality created. TypeScript is my language of choice, so everything will be written in it

import axios from 'axios'; export const getData = (): Promise<void> => {

const end = new Date(Date.now());

const now = new Date(Date.now());

const start = new Date(now.setMonth(now.getMonth() - 3));

return new Promise((resolve, reject) => {

axios.get('www.example.com', {

params: {

startDate: start.toISOString(),

endDate: end.toISOString(),

},

}).then(res => {

resolve(res.data);

}).catch(err => reject(err));

});

};

Very simply, this function getData is processing a start and end date, that is used with an axios HTTP request to get the data from the remote server. Obviously, this code is not super clean, and definitely can be refactored some. But, since we have the functionality in place, renaming and reorganizing should be straightforward.

Let’s start by refactoring this function to use async instead of promises. In order to properly bubble up the thrown errors, we don’t need to wrap it in a try / catch block.

import axios from 'axios'; export const getData = async () => {

const end = new Date(Date.now());

const now = new Date(Date.now());

const start = new Date(now.setMonth(now.getMonth() - 3));

const res = await axios.get('www.example.com', {

params: {

startDate: start.toISOString(),

endDate: end.toISOString(),

},

});

return res.data;

};

All of the date manipulation would be better extracted into a second function.

import axios from 'axios'; interface Dates {

start: string;

end: string;

} const getDates(): Dates => {

const end = new Date(Date.now());

const now = new Date(Date.now());

const start = new Date(now.setMonth(now.getMonth() - 3));

return {

start: start.toISOString(),

end: end.toISOString(),

};

} export const getData = async () => {

const dates = getDates();

const res = await axios.get('www.example.com', {

params: {

startDate: dates.start,

endDate: dates.end,

},

});

return res.data;

};

The date calculation was moved to getDates and an interface Dates was added to help describe the data being returned from the function. The code looks to be in a good spot, but some of the names are vague, making it challenging to understand what’s going on.

import axios from 'axios'; interface Dates {

startDate: string;

endDate: string;

} const calculateDateRange(): Dates => {

const now = new Date(Date.now());

const startDate = new Date(now.setMonth(now.getMonth() - 3)).toISOString();

const endDate = new Date(Date.now()).toISOString();

return {

startDate,

endDate,

};

} export const fetchDataForDateRange = async () => {

const dateRange = calculateDateRange();

const res = await axios.get('www.example.com', {

params: {

startDate: dateRange.startDate,

endDate: dateRange.endDate,

},

});

return res.data;

};

Now that the code is readable, let’s try to future-proof the code some by extracting out the amount of months back to backfill.

import axios from 'axios'; interface Dates {

startDate: string;

endDate: string;

} const calculateDateRange = (monthsToBackfill: number): Dates => {

const now = new Date(Date.now());

const startDate = new Date(now.setMonth(now.getMonth() - monthsToBackfill)).toISOString();

const endDate = new Date(Date.now()).toISOString();

return {

startDate,

endDate,

};

} export const fetchDataForDateRange = async (

monthsToBackfill: number,

) => {

const dateRange = calculateDateRange(monthsToBackfill);

const res = await axios.get('www.example.com', {

params: {

startDate: dateRange.startDate,

endDate: dateRange.endDate,

},

});

return res.data;

};

As seen, iteratively improving the code for the story has left it in a great spot to be tested, linted, and pushed up for a merge request. By tackling small, direct changes in a synchronous fashion, the code was quickly improved upon to ensure type checking and readability.

Conclusion

A lot can be gained from properly staging the implementation of chunk of code. If done correctly, readability and speed come at the same time. What are your thoughts on the iterative coding technique?