Across stacks, platforms, and domains (e.g., eCommerce, fintech, healthcare, etc.), the engineering team can take on two software development types: greenfield and brownfield development. Each development type brings specific opportunities and challenges that require adapting various components and best practices of the development processes.
Greenfield development involves creating a system for a business domain and use case without existing code, i.e., starting from scratch. Beyond requiring a specific stack, the novel software might still have constraints, such as using specific dependencies (e.g., third-party services, libraries, etc.). However, the squad works on a clean slate where every part of the system must be defined and implemented.
Greenfield development usually occurs when a new business venture is set up, thus needs new software, or when an existing business opts to reimplement a new version of its existing software.
Working on a greenfield codebase brings the following advantages:
- Implementing the latest development patterns and best practices for the stack that the application is built on. The new codebase can leverage the latest proven patterns and tools.
- Developing the codebase using all the team’s development and code conventions, including version control, testing, localization, and documentation. As a result, the codebase can represent the best of what the engineering team can produce.
- Unencumbered and predictable development thanks to the total ownership of every part of the codebase. The team possesses an in-depth understanding of every aspect of the code, thus can guarantee the application fulfills all acceptance criteria and can more easily debug any issue that might occur.
Engineering team members working on a greenfield codebase must factor in the following challenges:
- Slower velocity and high dependency between developers in the initial sprints. Since so much of the code implementation must be designed and implemented, progress on delivering working application code usually takes more time initially. Segregating work to avoid dependencies between each team member is usually harder to achieve.
- Defining and communicating efficiently the technical direction and vision for the software. With so many possible implementation options, the Team Lead is responsible for ensuring that developers clearly understand the technical approach to take. Without existing code to follow and clear instructions, it could lead to lengthy and unwanted rework.
To avoid common pitfalls, the following guidelines must be followed:
- DO aim to deliver functional features the earliest possible to get feedback.
- Do NOT over-engineer or aim for perfection when delivering features.
👉 Novel software must first and foremost be put into the hands of users. Therefore, in line with the fast-to-market iterative process principle, it is best to focus on delivering a “basic” version of a feature, i.e., an implementation that covers 80-90% of use cases but leaves room for unhandled edge cases. The goal is to get early feedback and continue iterating on the feature in subsequent sprints. In practice, many of the corner cases identified in the initial development might not be actual needs or might change as the codebase matures.
- DO prioritize writing the technical documentation from the beginning.
- Do NOT wait for the project hand-over to document the application.
👉 Technical documentation must be used as an efficient tool to communicate efficiently about the implementation approaches taken by the squad. Such documentation can be defined before any code is written (recommended), as it can speed up development or, at the very least when the feature is implemented.
Brownfield development involves working with previously created code. As a result, a brownfield codebase comes with externally defined code conventions, implementation patterns, and dependencies, i.e., the team had no say in how the application was architected.
Brownfield development usually happens when there is a need to continue the development of an existing application (e.g., adding new features), improve upon an existing application (e.g., modernizing the code structure, implementing a new UX/UI)
Working on a brownfield codebase brings the following advantages:
- Developing an application that runs in production and numerous users actively use. Unlike a greenfield codebase, which initially has no users since it is novel software, a brownfield codebase has been running in production for several months or even years. Hence, such as codebase comes with extensive usage data and allows to collect live feedback when improvements and new features are released.
- Progressively improving the codebase using all the team’s development and code conventions, including version control, testing, localization, and documentation. Thus, the team can follow the well-known principle: “Always leave the code better than you found it.”
- Discovering and learning implementation patterns that are novel to the team. Every third-party codebase is an opportunity to learn a different approach to solving technical challenges and adopt new patterns. The current development practices of the team are the sum of the experience and learnings during years of developing software projects with different teams.
Engineering team members working on a brownfield codebase must factor in the following challenges:
- Lack of documentation and knowledge on how the application works. More often than not, brownfield codebases come with limited to no technical documentation, thus requiring the team to invest significant time in codebase discovery phases (usually one sub-domain or context at a time) and even reverse-engineering exercises.
- Increased difficulty in changing the existing code to fit new requirements that can sometimes lead to complex refactoring. Similarly to documentation, more often than not, brownfield codebases come with low-single-digit or no test coverage. As a result, modifying existing code can lead to unexpected side effects and must be handled with extreme care.
- Constrained development options can sometimes lead to longer development cycles. For example, an existing application comes with preexisting constraints that cannot be easily changed (e.g., programming language and dependency version, minimum browser or mobile OS versions to support, etc.). As a result, the team cannot always leverage the latest tools or need to plan to upgrade the tools’ versions.
To avoid common pitfalls, the following guidelines must be followed:
- DO handle the releases of codebase changes with utmost care.
- Do NOT treat new releases as simply the creation of a product increment.
👉 Since a brownfield codebase comes with a user base, not breaking the application(s) for end-users is paramount. As a result, it is best to ensure thorough testing was made during the sprint and consider using progressive delivery mechanisms such as progressive rollouts, feature flags, etc. In addition, the squad must monitor the application closely to ensure no unexpected issues are left unchecked for too long.
- DO code refactoring iteratively and after adding adequate automated testing.
- Do NOT refactor entire parts of the codebase without any automated tests.
👉 Improving an existing codebase or fitting new requirements usually means changing the implementation. As a result, code refactoring is a natural by-product of brownfield development. However, the squad must embrace an iterative process, i.e., refactoring small areas of the code over multiple sprints, thus staying clear of spending weeks in refactoring work. In addition, adding automated tests must be a prerequisite to any code refactoring. It is crucial to crystallize how the system currently works before changing it. Regardless of how the application was architected, whether correctly or incorrectly, how the system works is what users currently need and use.