Recently, I stumbled upon a quote by Mark Twain that deeply resonated with me as both an iOS developer and a content creator: ‘Continuous improvement is better than delayed perfection.’ In my journey, I’ve started more and more to realize that striving for perfection can sometimes lead to inaction. This prevents us from sharing our work with the world. Instead, I learn to embrace the idea of continuous improvement in my projects. Progress and refinement, in small steps, are more valuable than waiting years for a masterpiece.
In this article, I’ll explore how this philosophy has shaped my approach to development and content creation.
As a developer, I’ve enjoyed crafting code that’s not just functional but also follows best practices to make the intent more clear, remove duplications, and so on. This desire, while admirable in its pursuit of excellence, can sometimes lead to an obsession over details, leading to what I call ‘the paralysis of perfectionism.’ It’s a state where the fear of imperfection stifles progress and innovation.
In software development, we often talk about Minimum Viable Product (MVP). It is desired to validate ideas with the least effort. However, it is often difficult for stakeholders and developers to identify the simplicity path. Feature overcrowding is a big temptation, as well as anticipating and addressing every possible scenario or user need. This can cause a product that’s over-engineered, over-budget, and behind schedule.
There is a constant debate in software development, about the pursuit of perfection, a tension between idealism and practicality. How do we pursue ideals like those of TDD, SOLID, Clean Code, and Clean Architecture, in a world of deadlines and evolving requirements?
Let’s take Emily for example, a fictional developer deeply committed to these principles. She’s dedicated to quality and excellence in all the work she does.
Emily was recently assigned to develop the authentication feature for an iOS app. There, she encounters numerous complexities. Her task, while crucial for security and user experience, is bugged with uncertainties and dependencies, particularly involving backend services. She collaborates with the backend team to refine regex patterns for email validation and engages in extensive discussions about business rules, trying to fill in the gaps. The validation is required but nobody knows for sure if there should also be a local validation performed. At the same time, it’s unclear what the API will look like in the end and what kinds of exceptions there will be.
Determined to create an impeccable solution, she found herself consumed by the details of every aspect of the task.
Emily engaged in lengthy discussions with the business team to understand the nuances of what constituted a valid email address in the context of their application. Should they allow email aliases? What about domain blacklisting? These discussions, while important, began to overshadow the primary goal of implementing a functional and efficient authentication system.
Emily’s focus on detail extended to her coding practices as well. She spent significant time pondering the architecture of the validation logic. Should email validation be an extension of the String class, or should Email be a distinct struct with its validation method? How should the code handle local versus remote validation, and what would be the most efficient and clean way to structure these different validation mechanisms?
Her story is not just about the pursuit of technical excellence but also about the pitfalls of losing sight of the bigger picture in software development.
Unfortunately, a planned demo of the work was coming up, but the authentication feature was still incomplete. She contacted an old friend and mentor for guidance on this issue. Emily shared the details of the struggles she was facing on the project.
As they went through Emily’s code, her mentor pointed out the intricate details she had included, praising her attention to detail but also questioning the necessity of some features at this moment of development. The mentor emphasized focusing on the core functionality of the authentication feature. Part of a good architecture is also to keep options open and allow stakeholders to decide later on the details. While attention to detail is essential, it should not come at the cost of project completion. The discussion led to a pivotal realization for Emily: perfectionism, while valuable, needs to be balanced with practicality.
Emily returns the next day to her project with a new perspective. She decides to simplify the authentication solution and focus on the essentials. The exceptions were filled in with some mocks, showcasing the UI implementation of these exceptions, while the BE was still deciding on the right error format.
This simplification paid off during the project’s demo. While the feature still had minor imperfections, it was functional and effectively demonstrated the core authentication process. It also allowed us to showcase the progress in other parts of the app. The demo’s success opened the door for further discussions on how to refine and enhance the feature in subsequent iterations, with clear insights into what was essential and what could be improved.
Based on her discussion with her mentor, she was becoming aware of the Pareto Principle, or the 80/20 rule, which she found to be an effective tool for managing her efforts. By focusing on the 20% of the work that would yield 80% of the results, Emily could allocate her time and resources more efficiently, ensuring that key functionalities were developed without getting bogged down by less critical details.
She learned the importance of tolerating a degree of imperfection to achieve overall progress.
Emily’s story is a reminder of the delicate balance between perfectionism and pragmatism in software development. Her journey highlights the importance of focusing on core functionalities, making strategic decisions, and being open to learning and adapting.
While striving for perfection can guide us toward excellence, it is the pursuit of continuous improvement that truly drives progress. We must not settle for ‘balls of mud’ after years of experience. We shouldn’t throw TDD, Clean Architecture, or SOLID principles out of the window in our fear of over-engineering.
As developers, we should acknowledge that perfection is an ideal — a north star worthy to pursue — it can help us navigate our projects with more confidence and flexibility. It’s in the journey, with its learning and adaptability, where the real growth and success lie.