Software Development: 8 steps to better feature development process
As a software development company, we are constantly working to grow team maturity, which has led us to adopt better practices in our projects, such as Site Reliability Engineering and the observation of DORA metrics.
In many projects, the problem isn't just technical - it's chaos before the first commit. What we call 'lack of productivity' is often just a lack of clarity about what needs to be done and when. This is why we've been discussing how we can improve the maturity of individuals. In other words, how can we make our developers write better code in less time?
TDD, refactoring, unblocking, and debugging — all of these techniques help. Yet, they are easier said than done. This is why we created a step-by-step guide to help professionals seamlessly incorporate this feature development process into their routine.
Hopefully, with this guide, you will be able to better plan and write features using software delivery best practices. 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, etc.), 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 understand both the feature requirements and the surrounding code context, it's time to plan your solution. At this point, resist the urge to start coding immediately.
Instead, think through the problem more deeply:
- Draw diagrams on paper to visualize your approach;
- Map entity relationships and data flows;
- Sketch how different components fit together;
- List all possible scenarios and states;
- Review existing code more closely if you need additional context.
Once you have an initial solution idea, evaluate any external dependencies:
Library Selection:
- Review documentation to confirm the required features are available;
- Verify the library behaves as expected in your use case;
- Check if the library is well-maintained by examining recent commits and open issues;
- Confirm compatibility with your application (language version, framework version, license, etc.);
Breaking Down the Solution:
- Divide your solution into smaller, manageable blocks;
- Document these as subtasks within your main task;
- Consider edge cases, exceptions, integrations, and validations;
- Identify existing functionalities that might be impacted and add testing subtasks.
Non-Functional Considerations:
- Evaluate usability, performance, cost, data integrity, reliability, monitoring, and serviceability;
- If you identify potential risks in production, collaborate with your Tech Lead (TL) to assess impact and create mitigation subtasks
The last part of this planning is actually one of the most important: prioritizing the order in which 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. For example, a complex animation that you are unsure 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 imagining what other similar solutions would make things simpler or 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 Test-Driven Development (TDD) — writing tests before implementing functionality — 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 suite that will be essential in the next step.
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 that 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.
This whole process is detailed in more depth in the book by the co-founder of Vinta, in which he explores how technical teams can evolve their way of delivering software without depending on positions or hierarchies. You can find out more about the contents of the book Strategic Engineering Software here.
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 pace 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 the docs that need updating and creating new ones. You should also consider writing an ADR (Architecture Decision Record) to keep track of the changes and to sync your teammates about those.
To sum up
Here at Vinta, implementing this step-by-step guide across our software development projects has yielded exceptional results and positive team feedback.
For experienced developers, this framework likely formalizes what you're already doing intuitively, but it still serves as a valuable periodic checkpoint to ensure you're maintaining best practices. For developers at all other experience levels, we recommend following this feature development process guide methodically for each new feature until the process becomes second nature.
By incorporating these practices into your development workflow, you'll not only write better code but also contribute to a more mature, reliable engineering culture. The investment in proper planning and structured development today will pay dividends in reduced technical debt and more sustainable projects tomorrow.