Software Development: 8 Steps to Make Features Better and Faster
Filipe Ximenes • 29 April 2022
We know that as a software developing company, Vinta's strongest feature is its people. This is why we are constantly working to grow team maturity, which led us to adopt better practices in our projects, such as Site Reliability Engineering and the observation of DORA metrics.
Simultaneously, we've also been discussing how we can improve the maturity of individuals. In other words, how we can make our developers write better code in less time.
TDD, refactoring, unblocking, debugging... all of these techniques do help. Yet, they are easier said than done. This is why we come up with a step-by-step guide to help professionals include this process seamlessly into their routine.
Hopefully, with this guide, you will be able to better plan and write features. Keep reading to learn more!
1. Understand the feature
First and foremost, make sure you understand what you are going to do. Carefully read the whole feature user story, open and read any external documents referenced (RFCs, docs, code snippets, …), inspect images, and study the design assets.
If it's a change in an existing feature, play a bit with it, testing different parameters and flows. If necessary, make questions tagging stakeholders. If it's not possible to wait for answers, try notifying people via chat or scheduling a quick meeting.
If you have multiple features planned for your sprint, run this step for all of them before you even start working on one. By doing this you will gain an overview of your tasks that will allow you to:
- Have time to gather the missing information in stories (and do this asynchronously without a rush);
- Prioritize the stories that are ready for development while you wait for more context on others;
- Better track your pace and communicate delays along the sprint.
2. Understand the software development context
Every feature exists in the context of a broader software development project. It's not possible to plan how you are going to implement a feature if you don't understand the parts of the code it will need to interact with.
To do this, carefully read the whole flow where that code will be placed and make sure you understand what is happening in each part. At this point, there's no need to dig deeper into implementation details, just read the function/method names to get a broad understanding of the flow.
For instance, if you are working on a serializer, start from the route, read the controller and the queries, then read all the code in that serializer. Do not move on to the next steps before you are confident you understand both the feature and the code context.
3. Plan a solution
Now that you are confident about what you are going to do and about the surrounding code, it's time to think about how your solution will look like. At this point, we don't want to start writing any code.
Just think it through, draw diagrams on paper, map relationships, sketch how things fit together, list all possible scenarios and states, and dig a little deeper into the existing code if you need more context.
Once you have an initial idea, check if you are going to use any external libraries. If so, review the library documentation to confirm it has the features you will need and that it behaves as you expect.
Double-check if the library is well-maintained, and review the open issues. Check also the library compatibility with your application platform (language version, framework version, license, etc.).
Now it's time to break down the solution into smaller blocks. As you do it, write these down as subtasks of the main task. Think about edge cases, exceptions, integrations, and validations. Moreover, check other functionalities that might be impacted or that will need more testing to confirm they didn't break, then also write these down as subtasks.
Finally, consider all the non-functional aspects of the feature: usability, performance, cost, data integrity, reliability, monitoring, serviceability, etc. If you foresee any risk of worsening those aspects in production, ask your Tech Lead (TL) to review the impact with you and devise subtasks to tackle this too.
The last part of this planning is actually one of the most important: prioritizing the order things will be tackled. To do this you need to evaluate the importance and risk of each activity and plan an MVP.
What is the minimum amount of work/activities you can do that delivers the most value? What is absolutely crucial to the feature? What is less important and could be left for later? Here are some tips on how to evaluate risk:
- Is it time-consuming? This can either indicate that it needs to move up or down in priority depending on the importance;
- Can this be a blocker? Again it can either mean it needs to be moved up or down in priority depending on the importance;
- If a task requires experience with a part of the code or a third-party lib that you are not familiar with, it might be a good idea to give it more priority to avoid late blockers;
- Version zero can often be more forgiving with the interface unless, of course, the UX represents a risk for the feature value. Eg.: a complex animation that you are not sure how to do.
While running this process you will often identify parts of the feature that are too complex or time-consuming. When you notice this try to imagine what other similar solutions would make things simpler/faster.
4. Validate your solution
For more complex features or if you are not confident about your solution, it's a good idea to validate it with someone else.
In most cases, a TL will be the best person to review it because they have the technical context of the software development process. Yet, sometimes it can be done with a manager, a designer, or some other project stakeholder.
Write a paragraph or two explaining how you are planning to do the feature including some but not all technical details and send it for validation. When it's something that cannot be summarized in a couple of paragraphs, it's probably a good idea to schedule a 10-minute desk check to make things easier for everyone.
This is a good moment to report on the complex and time-consuming things you noticed. Confirm with stakeholders if the simpler solution you came up with really works. Sometimes, it will make sense to completely remove that part of the feature or delay it to another moment.
Make sure any useful delayed parts are tracked as new tasks with proper context, for them to be prioritized later.
5. Make it work
Now it's time to start writing code. For now, focus only on the MVP you've defined. It's very important that you don't go beyond it. The goal is to have a working prototype that validates your assumptions.
It's important to write the least code possible — we are still learning about how the new feature fits in the existing code and validating the initial architecture we had in mind.
Less code means it's easier to experiment with other approaches and to change the architecture in case we end up not liking the initial one. But don't go trying architectures until you have a working prototype. In fact, if it makes sense, consider writing this first version as a script completely decoupled from the application and then transplant it back.
Pace your work, and don't get ahead of yourself. Because you've planned the execution you can, one by one, pick the topmost subtask you created and work on it individually. Once you've picked an activity, focus on getting that single thing done and forget about any other tasks.
When you are done, write a commit message and mark the subtask as completed, it's important to celebrate progress! Using TDD will make this whole process much easier because it follows the same philosophy of gradual, paced work. It will also help you build a comprehensive test suit that will be essential in the next step (refactoring!).
As you make progress you will identify unmapped edge cases and new requirements. It's important that you don't work on them immediately. First, create a new subtask and prioritize it among the others. This will allow you not to deviate from focus, reduce the cognitive load and prevent you from getting anxious.
6. Time to refactor
With a working first version, it's time to work on code quality and prepare the ground for the definitive solution.
At this point, you should not add any new features. Try architectures you think might better suit the problem, reorganize interfaces, rename variables, isolate concerns, encapsulate implementation details, and make sure the code is comprehensible and clean.
Because you've written tests you can be confident the work you've done so far works and that you are not breaking other stuff. Since you only have the bare bones of the feature, refactoring should require minimal effort. Once you are comfortable with the architecture and quality of the code, move on to the next step.
7. Fill in the gaps
Now it's time to close up the feature. Work on the items you judged as non-critical. Fill in the details, nice-to-haves, and polish the interfaces. Keep the paced work, one task at a time, use TDD, and celebrate progress by writing commits and marking subtasks as done.
Review the code and check if you should add log messages or if some parts would benefit from having more code comments.
8. Update and create docs
All good, let's now close the feature up by reviewing docs that need updating and creating new ones. You should also consider writing an ADR to keep track of the changes and to sync your teammates about those.
To sum up
Here at Vinta, we've been using this step-by-step guide in our software development projects with very good results. We also had nice feedback from the team. Many senior developers will find that it describes pretty much things that they do automatically, regardless, you might find it useful to periodically use this guide as a checkup of your practices.
For everyone else, we recommend following the guide step-by-step and reviewing it for every new feature you work on until repetition fixates the process in your mind and it becomes part of your daily routine.
Like this post? Follow us on LinkedIn and stay tuned to all of our tips and news.