Revolution vs. Reform: The Endless Debate in Software Development

Disclaimer: The ideas in this text are mine, but I ran it through ChatGPT to improve the readability of my english and to make it more enjoyable to read in general.

Introduction:

In the realm of software development, an age-old debate continues to stir discussions among developers: revolution versus reform. As someone who aligns with the revolution faction, I firmly believe that periodically rewriting software is essential. The act of starting from scratch not only allows for the integration of newfound knowledge and ideas but also enables the creation of modular components that can be preserved for future iterations. While the reform approach, for example implemented through the "strangler refactor pattern," emphasizes stability, I often find that it leads to convoluted code over time. In this blog post, I'll delve into the merits of revolution, while also acknowledging the counter perspective and the importance of striking a balance.

The Case for Revolution

Software development, like any other field, benefits from periodic revolutions. A complete rewrite provides the opportunity to reimagine and redefine a project, fully incorporating the lessons learned along the way. By starting from a clean slate, developers can integrate innovative ideas, leverage emerging technologies, and adopt best practices that have evolved since the original implementation. Furthermore, a revolution allows for the creation of modular and decoupled components, laying the foundation for future iterations that may require incremental enhancements or replacements.

The Pitfalls of Reform

The reform approach, epitomized by the "strangler refactor pattern," emphasizes the gradual replacement of outdated or suboptimal code components while maintaining overall system functionality. While this strategy offers stability and minimizes disruption, it can lead to challenges over time.

For instance, let's consider a real-world example: imagine you have a popular mobile banking application, and the development team decides to enhance the transaction history feature. They begin by refactoring the existing codebase, replacing the outdated database structure with a more efficient one, and introducing new sorting algorithms for better performance.

However, as the team progresses with the refactoring process, they come across another improvement opportunity. They realize that by integrating a machine learning algorithm, they can offer personalized spending insights to users. This addition also requires significant changes to the parts in the existing codebase and data model where it is used. The team decides to refactor the code further, introducing the necessary components for the machine learning functionality.

Over time, the codebase becomes a blend of the original code, the initial refactored code, and the subsequent modifications. Developers now have to navigate through multiple layers of historic changes and understand the interactions between different versions of the code. This accumulation of multiple solutions within the codebase can introduce confusion, increase complexity, and complicate future refactoring efforts.

Furthermore, let's draw a parallel to the real world. Imagine you are in the market for a new bicycle, and the manufacturer releases a new version with the addition of a powerful electric motor. However, upon closer inspection, you discover that the new version is essentially the same as the previous one, but with the electric motor attached. In this scenario, you might question whether this incremental change justifies investing in the new version, especially when compared to other bicycles in the market that offer more significant improvements and innovations like a more lightweight carbon fiber frame or advanced suspension systems for improved comfort and control.

Similarly, in software development, the reform approach of attaching new features to an existing codebase may not always provide substantial value to users or the development process. It could result in incremental changes that look great from the user perspective but ultimately fail to address underlying design flaws, leading to band-aid solutions and accumulating technical debt over time.

While the reform approach can offer stability and minimize disruption, it is essential to consider the potential downsides and evaluate whether the changes truly align with the project's long-term goals and users' needs. Striking a balance between incremental improvements and fundamental reevaluations can be the key to building successful and sustainable software projects.

Striking a Balance

As with any debate, it is crucial to consider alternative viewpoints. The reform approach, with its incremental improvements, can be beneficial for mature projects with stable functionalities and well-designed architectures. In such cases, a revolution might be disruptive and impractical, as it risks introducing new bugs and requiring extensive retesting. Gradual refactoring enables developers to iteratively enhance the codebase, mitigate technical debt, and ensure a smoother transition for end-users. Moreover, it allows for the preservation of existing knowledge and ensures the software remains operational throughout the process.

Conclusion

In conclusion, the debate between revolution and reform in software development is a nuanced one. The revolution approach, with its clean slate and integration of new ideas, can be highly beneficial, particularly in the early stages of a project where rapid experimentation and learning take place. However, as projects mature, it becomes crucial to transition towards a reform approach that emphasizes incremental improvements and stability. Gradual refactoring allows developers to mitigate technical debt, enhance existing functionalities, and ensure smoother transitions for end-users.

That being said, it is essential to avoid rigidly adhering to one approach throughout the lifetime of a project. The decision to adopt either a revolution or reform approach should be made on a case-by-case basis, considering the specific circumstances and goals of the project. Early on, a revolution may be necessary to establish a solid foundation and incorporate novel ideas. As the project progresses and matures, transitioning to a reform approach can provide stability and foster incremental enhancements.

Nevertheless, it is crucial to remain flexible and open-minded. Even during the later stages of a project, there may be instances where a revolution is warranted, such as when fundamental design flaws hinder progress or when a significant technological shift demands a complete overhaul. Conversely, in the early stages of a new project, a reform approach might be more appropriate if the foundation is already well-established or if time and resource constraints discourage starting from scratch.

Ultimately, the success of a software project hinges on careful consideration of the unique circumstances at hand. Striking a balance between revolution and reform, while keeping an eye on the project's overall objectives, ensures that developers can adapt their approach to maximize efficiency, maintain quality, and deliver robust software solutions. By approaching each project with a thoughtful and adaptable mindset, software developers can navigate the complexities of revolution and reform, ultimately building software that stands the test of time.

Adventures in Math

Over the weekend I went ahead and rewrote a bunch of math code we use in my work to get it into a clean state. It's mainly a library for some computational geometry which uses some other known libraries (geo and glam) as it's foundation. The main goal was to create a reasonable geometrical hierarchy in both 2D and 3D contexts. Currently these hierarchies merely feature rays and line segments, but surfaces will be added soon. Here are the objects included in these hierarchies in the current state of the library:

2D

  • Ray
  • Line Segment
  • AABB

3D

  • Ray
  • Line Segment
  • Plane
  • Coordinate System
  • AABB

The most important feature as of now is the intersection sub-crate. Which implements all kinds of intersection between these objects (where they make sense).

The implementation was rather fun and I open sourced it under AGPL3+ (hehe) but I doubt it will be useful to anybody else since I made some decisions (tolerances and co) which might not be ok for others. Overall I'm happy about the project, mainly because it's finally tested code (100+ tests already! <3)