--- title: Slowftware Development date: 2024-10-04 draft: false --- # Going fast, slowly I recently came across an almost [10-year-old blog post][3] on Varnish HTTP Cache (which was released nearly 20 years ago). In that post, the author shares the LoC (Lines of Code) analysis of Varnish and talks about the average LoC per hour per developer and how the LoC metric has been used before to judge productivity. And although the content of the blog post is quite captivating, and although the LoC metrics themselves are quite interesting and worth looking at, (for example, ~22% is documentation and ~22% is testing), but ultimately what captivated me the most, is the following quote (which I hadn't heard before reading this post): > "Perfection is attained, not when there is nothing more to add, but when there is nothing more to remove."
> --- _Antoine de Saint-Exupéry’s observation_ **So how does a perfect software look?** It's a well-known fact, that software development is quite messy, and unlike any other branch of engineering, it's also the most tolerating failures and short-comings in design (it shouldn't, but that's a topic for another time). There is no perfect software, and there will be none in the future, but there are many good software products (and software developers) out there, and for anything good to develop, it needs time, **lots** of time. The time required is dependent on many factors, including but not limited to, the developer's previous experiences, the understanding of the problem, the time needed to theorize a solution, and the time to code that solution. The first factor, "previous experience," isn't controllable. (at least not for this post's purposes).
The last factor, "time to code," is the least important, it can be adjusted (with hiring) or planning costs for the estimated time required.
The other two factors are the most influential. And the ones that are affecting how correct the "time to code" estimation will be.
If you don't understand the problem at hand or have developed the wrong solution (whether it's technically the wrong implementation or design, or conceptually solving the wrong problem), the time spent grows (linearly at best).
So, _sometimes_ it's faster to be slower in understanding the problem, theorizing a solution, mapping the solution components to the proper design patterns, and then starting to code that solution. There will always be a discussion on the time this will take and how much market value is being lost because we are not reacting fast. That's not to -[anecdotally][9]- mention the cost of having multiple planes nose-dive because the software solution that needed to be implemented quickly to capitalize on the market wasn't properly designed with redundancies and proper quality control. Now you start to see how going slower is **faster** and **cheaper**.
So, how to "slowly" write the best solution? # The Agile Manifesto In 2001, _The Agile Alliance_, a group of 17 software practitioners met at a resort to discuss lightweight development methods (i.e., Extreme Programming, Scrum, Lean), these lightweight development methods evolved in reaction to the prevailing _heavyweight_ methods (often referred to collectively as waterfall). They wrote down the [Manifesto for Agile Software Development][15], and the 12 [Principles behind the Agile Manifesto][16]. And although these lightweight methods already preceded the publication of the Agile Manifesto, they came to be collectively referred to as the agile software development methods. Few things have reached far and beyond the reach of the Agile Manifesto in software development, and fewer things have had more impact than the Agile Manifesto in recent years on software development and work methodologies (both good and bad). Over the years, there has been criticism and debate over the agile values, and how they can undermine various disciplines of the software development process, namely documentation[^1] and testing. Personally, I think that most of the criticism is targeted towards the interpretation or to be more specific, the misinterpretation of the agile principles. That's not to say that there are no merits to the criticism, but to actually inspect and try to correct the shortcomings of the agile framework. In an [article][18] titled, "**Have we taken agile too far?**", [Colin Bryar][19] and [Bill Carr][20] write about their criticism of the Agile process, especially in the crucial early stages. > Agile is a powerful process for product development, but many organizations are taking it too far and using it > to avoid careful planning and preparation. They elaborate on this deposition that "**Speed Isn't Everything**", they inspect a misinterpretation of the principle "**Sustainable development, able to maintain a constant pace**", where the focus is on maintaining a constant pace rather than being sustainable. > The fundamental problem with agile, as many companies use it, is that its relentless pace biases developers. > They want to get out a minimum viable product in only a few weeks, so they skimp on scoping out just what the > product should accomplish. Or worse, in our experience, they make two kinds of compromises. > > First, rather than take the time and uncertainty to develop a new capability, they go with the skills they currently > have. They accept their existing constraints, which automatically limits the potential for a high-growth offering. > > Second, they curtail their ambitions on the product. Instead of a major breakthrough, they tend toward only > incremental improvements on existing offerings. Or if they do go bold, their minimum viable product isn’t really > viable at all, so customers can’t give realistic feedback. The developers haven’t had time to do their homework > and prepare something that’s sustainable. In order to avoid these misinterpretations and reap the benefits of using agile, they suggest: > We’re not arguing that companies should throw agile out the window. It’s still a highly effective tool for product > development, especially software-driven offerings. Many of its principles and processes have been used successfully > by Amazon and other companies.
> ...
> The best solution, then, is to combine agile with something like working backwards. Amazon, for example, has learned > to use the working backwards process for idea development, but then follow agile to build and ship the product. # Working Backwards Working Backwards is a systematic way of vetting ideas to create new products. It starts by defining the customer experience, then iteratively work backward from there until the team achieves the clarity of thought around what to build. In their book titled "[**Working Backwards**][21]", authors [Colin Bryar][19] and [Bill Carr][20], describe (among many other principles and practices) the "Working Backwards" strategy and how it was developed, its key tenets, and the practices that emerged from working backward (i.e., the banning of PowerPoint presentations at Amazon). > Working backwards emerged in 2004: Amazon’s e-commerce strategies had proven successful, and the company was > aggressively seeking new opportunities with a large potential market. Where should it look? > > Rather than jumping into developing a plausible product — what an agile mindset might encourage — the company preached > going slow to go fast. > > The working backwards approach requires a fully realized vision of a proposed product, embodied in a written press > release for the product’s launch. This felt wrong, even unnatural, to software developers and product managers who > wanted to get going on coding already. > > The practice has stuck: To this day Amazon works backwards from what it thinks will delight customers, even if it > currently lacks the capabilities to make that product. The Kindle e-reader, > AWS cloud computing services, and the Echo voice assistant with Alexa all came from working backwards. --- [source][18]. The brilliance of this strategy is that the iterative process of drafting, reviewing, and refining the PR/FAQ documents forces the teams and management to take the time to deeply understand not only the problem at hand but also the vision of what the product **is** to be, not what the product **might** be (when developed with agile MVP principles). It ensures that everyone involved in the development process, from engineers to marketers, is focused on delivering a product that genuinely meets customer needs (which is a core principle in agile, but occasionally miss-placed and miss-used). So, how to "slowly" write the best "working-backwards" solution? # Worse is better In 1989, Professor [Richard P. Gabriel][4] wrote an [essay][5] to describe the dynamics of software acceptance. It is the argument that software quality does not necessarily increase with functionality: that there is a point where less functionality ("worse") is a preferable option ("better") in terms of practicality and usability. Software that is limited, but simple to use, may be more appealing to the user and market than the reverse. In his [essay][5], he sums up the worse-is-better philosophy as: > - **Simplicity**: the design must be simple, both in implementation and interface. It is more important for the > implementation to be simple than the interface. Simplicity is the most important consideration in a design. > - **Correctness**: the design must be correct in all observable aspects. > It is slightly better to be simple than correct. > - **Consistency**: the design must not be overly inconsistent. Consistency can be sacrificed for simplicity > in some cases, but it is better to drop those parts of the design that deal with less common circumstances > than to introduce either implementational complexity or inconsistency. > - **Completeness**: the design must cover as many important situations as is practical. All reasonably expected > cases should be covered. Completeness can be sacrificed in favor of any other quality. In fact, completeness must > be sacrificed whenever implementation simplicity is jeopardized. Consistency can be sacrificed to achieve completeness > if simplicity is retained; especially worthless is consistency of interface. This philosophy has been miss-interpreted[^2] (past the catchiness of the phrase), as advocating for doing a bad job with code or architecture. In truth, what the philosophy embodies is a stronger emphasis on quality and true agile principles (incremental change and sustainable development pace). To elaborate on "simplicity over correctness": > Looking through the definition of "Worse Is Better," you might be struck by the immense value it places on > correctness, more than most other approaches that state it is a value, but don't support it beyond lip service. > The "Worse Is Better" position that "the design must be correct in all observable aspects" is fairly clear on where > it stands on correctness. But it is not uncompromising, as the "better to be simple than correct" > compromise acknowledges. > > When we state characteristics of a design style, such as simplicity and correctness, these characteristics are > often in sympathy, but sometimes they are in tension. How do we resolve that tension? Simplicity is considered > the most important characteristic. One consequence of simplicity is improved correctness: not just that it is easier > to create correct code, but that it is easier to see that code is correct. As Tony Hoare observed, > "There are two ways of constructing a software design: One way is to make it so simple that there are obviously > no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies." > It is also easier to correct something that is incorrect when it is simple than when it is complicated. > > Furthermore, it is easier to discard and replace something that is simple and incorrect. Because it's simple, > we understand it and can replace it with confidence—than something that is complicated and incorrect — because > it's complicated, we do not understand it, so replacement evokes fear. There is another side to this as well: > favor something that is simple and correct over something that is complicated and correct, > because, whatever our aspirations, the latter is less likely to be or to remain correct. > > In short, the (minor) ordering of simplicity over correctness better serves the longer term than just emphasising > correctness alone. --- [source][33]. The essence of the philosophy is, > **when tradeoffs are required in a software system, > priority should go to implementation simplicity at the expense of completeness or correctness.** --- [source][8]. This philosophy was observed in the [UNIX philosophy][13] - Make it easy to write, test, and run programs. - Write programs that do one thing and do it well. - Write programs to work together. And also propagated to many modern languages. For example, [the zen of python][6] emphasizes: - Beautiful is better than ugly. - Explicit is better than implicit. - Simple is better than complex. - Complex is better than complicated. - If the implementation is hard to explain, it's a bad idea. - If the implementation is easy to explain, it may be a good idea. Now, taking the goals of simplicity then correctness as the highest priorities in the solution,
So, how to "slowly" write a "working-backwards worse" solution? # The Big Ball of Mud > If you think good architecture is expensive, try bad architecture. In a [paper][10] published in 1997, Authors [Brian Foote][11], and [Joseph Yoder][12] examine the most frequently deployed architecture, dubbed "The BIG BALL OF MUD" > A BIG BALL OF MUD is a casually, even haphazardly, > structured system. Its organization, if one can call it that, is dictated more by expediency than design. Yet, > its enduring popularity cannot merely be indicative of a general disregard for architecture. In this paper, the authors examine many of the forces that affect the architecture, including: - Time. - Experience. - Turnover. - Skill. - Complexity. - Change. - Cost. And how these forces can affect the development life-cycles.
The uniqueness of this paper is that it doesn't describe the resulting systems as anti-patterns. > These are not anti-patterns, at least in the customary sense. They are not straw men. Instead, they seek to > examine the gap between what we preach and what we practice.
> ...
> BIG BALL OF MUD might be thought of as an anti-pattern, since our intention is to show how passivity in > the face of forces that undermine architecture can lead to a quagmire. However, its undeniable popularity > leads to the inexorable conclusion that it is a pattern in its own right. It is certainly a pervasive, recurring > solution to the problem of producing a working system in the context of software development. It would > seem to be the path of least resistance when one confronts the sorts of forces discussed above. They go even further as to observe that this specific pattern might have a survival advantage over good code, not a position most companies want to be in, since the cost to maintain, and time to fix bugs increases drastically the longer the project runs. Moreover, it can (and almost always) lead to the "TOTAL REWRITE" (which is also as expensive -if not even more expensive- and takes a lot of time too). > As per CONWAY’S LAW [Coplien 1995], architects depart in futility, while engineers who have mastered the muddy details > of the system they have built in their images prevail. [Foote & Yoder 1997] went so far as to observe that inscrutable > code may, in fact, have a survival advantage over good code, by virtue of being difficult to comprehend and change. > This advantage can extend to those programmers who can find their ways around such code. There are always exceptions of course, for example, the paper mentions how the [Wiki-Web code at c2.com][22] has its humble origins as a "THROWAWAY CODE" that succeeded beyond the author's expectation, but this is not the rule. The observations in the paper, and the prevalence of the "BIG BALL OF MUD" or some dialect of it, can almost always be traced back to the previously mentioned forces (Time, Experience, Complexity, ...), and how their application always undermines the architecture of the system and results in these undesirable patterns. Although the publishing of this paper precedes the publishing of the Agile manifesto, it's hard not to see the similarities between the common misinterpretation of the Agile principles, and the causes/forces that lead to develop big balls of mud. **A good takeaway from this paper is how important software architecture is to maintaining a healthy system, that can be cost-efficiently maintained and relatively fast to fix.** > It is not our purpose to condemn BIG BALLS OF MUD. Casual architecture is natural during the early > stages of a system’s evolution. The reader must surely suspect, however, that our hope is that we can > aspire to better. By recognizing the forces and pressures that lead to architectural malaise, and how and > when they might be confronted, we hope to set the stage for the emergence of truly durable artifacts that > can put architects in dominant positions for years to come. The key is to ensure that the system, its > programmers, and, indeed the entire organization, learn about the domain, and the architectural > opportunities looming within it, as the system grows and matures. In other words: > Don't leave "broken windows" (bad designs, wrong decisions, or poor code) unrepaired. Fix each one as soon as it is > discovered. If there is insufficient time to fix it properly, then board it up. Perhaps you can comment out the > offending code, or display a "Not Implemented" message, or substitute dummy data instead. Take some action to prevent > further damage and to show that you're on top of the situation. > > We've seen clean, functional systems deteriorate pretty quickly once windows start breaking. There are other factors > that can contribute to software rot, and we'll touch on some of them elsewhere, but neglect accelerates the rot faster > than any other factor. --- [source][23] If the architecture is clear (having boundaries) and clean (following design patterns), it's easy-_ish_ to fix the code, even if the code is complex or complicated. But if the architecture is not clear or clean, even the most crystal clear and clean code will eventually become a big ball of mud, it's the law of entropy. It is worth mentioning here that, having a design pattern reference guide at arm's length, can go a long way toward identifying the design pattern(s) that could be employed to solve different or common programming problems. Personally, I keep the refactoring.guru [book][24] (and [website][25]) close by. So, how to "slowly" write a "working-backwards worse" solution that isn't a "big ball of mud"? # Re-disposable code In a series of blog posts titled: 1. [Write code that is easy to delete, not easy to extend.][26] 1. [Write code that’s easy to delete, and easy to debug too.][27] 1. [Repeat yourself, do more than one thing, and rewrite everything.][28] The [author][29] suggests that instead of focusing on code reusability, one should focus more on code disposability. The reasoning behind this is not that it's just more fun to delete code down the line (although it's really fun), but with the main focus on disposability, it results in a system that is actually more reusable, and less tightly coupled. They start with the following quote: > Every line of code is written without reason, maintained out of weakness, and deleted by chance.
> --- Jean-Paul Sartre’s Programming in ANSI C. They then give the steps that one can use when developing a disposable system. These steps are summarized in the beginning as follows: > To write code that’s easy to delete: repeat yourself to avoid creating dependencies, but don’t repeat yourself > to manage them. Layer your code too: build simple-to-use APIs out of simpler-to-implement but clumsy-to-use parts. > Split your code: isolate the hard-to-write and the likely-to-change parts from the rest of the code, and each other. > Don’t hard code every choice, and maybe allow changing a few at runtime. > Don’t try to do all of these things at the same time, and maybe don’t write so much code in the first place. These steps and concepts not only embrace the resulting "balls of mud" and keep them at bay but also utilize the resulting effects they have on the system to improve system usability by improving the system disposability. > A system where you can delete parts without rewriting others is often called loosely coupled, > but it’s a lot easier to explain what one looks like rather than how to build it in the first place.
> ...
> A healthy code base doesn’t have to be perfectly modular. The modular bit makes it way more fun to write code, > in the same way that Lego bricks are fun because they all fit together. **A healthy code base has some verbosity, > some redundancy, and just enough distance between the moving parts so you won’t trap your hands inside.** > > Code that is loosely coupled isn’t necessarily easy-to-delete, but it is much easier to replace, > and much easier to change too. One of the interesting ideas is the discectomy between clean and boring code, and how sometimes clean code isn't desirable since it's harder to debug than boring code, or as the author puts it: > The problem is that “clean” is highly contextual in meaning. Clean code can be hard-coded into a system, > and sometimes a dirty hack can written in a way that’s easy to turn off. Sometimes the code is clean because > the filth has been pushed elsewhere. Good code isn’t necessarily clean code.
> ...
> Instead of clean, we want boring code where change is obvious.
> ...
> It is not to say that clean code is bad, but sometimes the practice of clean coding is more akin to sweeping > problems under the rug. Debuggable code isn’t necessarily clean, and code that’s littered with checks > or error handling rarely makes for pleasant reading.
> ...
> **Code that’s easy to debug is easy to explain.** Another interesting idea is the misinterpretation of "[Don't Repeat Yourself (DRY)][30]" as "Don't Copy Paste". There is a complete post about the misuse of DRY and how it complicates the system design and, in return, makes the system less reusable and less disposable. A healthy level of repetition and redundancy can a lot of times help in maintaining a system that is disposable and reusable; the "[Avoid Hasty Abstraction (AHA)][31]" is an example of this. It can also be seen how these concepts are changing depending on the situation. Sometimes DRY is better than AHA because it encapsulates a ball of mud that can be disposable or replaced, yet sometimes AHA is better because [a wrong abstraction][32] is even more expensive than no abstraction. **The bottom line is: There are no silver bullets, use your own judgment.**
So, how to "slowly" write a "working-backwards boring worse" solution that is a "disposable ball of mud"? # Slowftware development _Pronounced: /sloʊf(t)ˌwɛə/_: is the answer to the previous question by using the previously mentioned concepts to: - Work-backwards from the customer's or client's point of view. - Identify the right problem that needs to be solved. - Simplify that solution. - Iterate on the simplified solution with different stakeholders. - Start conceptualizing an architecture -based on design patterns if possible-. Then, once the theorized solution has been decided: - Write a disposable boring code, that is also easy to debug (and test). - Follow the agile principles, not the agile framework meetings. Iterate with the customer, once a meaningful chunk of the work has been delivered, and welcome late changes, while rewriting your code to be more boring, more disposable, and more testable. Put simply, use the previously mentioned concepts and techniques, to develop disposable, reusable, sustainable software in a truly agile manner -which occasionally can happen in an agile framework-.
Apply those ideas to make both the development environment and output products better, and more sustainable.
Make sure that when you are moving, you are moving in the right direction towards a clear goal, and not just moving. This is not a silver bullet to fix current software development, it's not a new paradigm for development, and it's not a step-by-step checklist to deliver correct, sustainable, reusable software fast. This is just the collection of these old ideas, put together in an attempt to make software development more concise and more fun, while maintaining a sustainable pace of development with foresight on the end goals, and trying to be as fast as possible, without sacrificing or compromising on quality. So next time, you feel that the way agile is being used in a team or a company isn't working as intended, try suggesting or incorporating these ideas in the development process, which I call "slowftware development". _Special thank to [Abdallah Dorra][34] for proof-reading and making suggestions on unclear sections._ --- [^1]: One example: In a letter to IEEE Computer, Steven Rakitin expressed cynicism about agile software development, calling it "yet another attempt to undermine the discipline of software engineering" and translating "**working software over comprehensive documentation**" as "we want to spend all our time coding. Remember, real programmers don't write documentation."
This is disputed by proponents of agile software development, who state that developers should write documentation if that is the best way to achieve the relevant goals, but that there are often better ways to achieve those goals than writing static documentation. [source][17]. [^2]: one famous misinterpretation, a variation adopted early on by Facebook and attributed to its founder, Mark Zuckerberg: "Move fast and break things and the idea was if unless you're breaking some stuff, you aren't moving fast enough".
They used it to build software as quickly as possible, even sacrificing correctness for the sake of a quick release. [3]: https://varnish-cache.org/docs/trunk/phk/thatslow.html [4]: https://en.wikipedia.org/wiki/Richard_P._Gabriel [5]: https://www.dreamsongs.com/RiseOfWorseIsBetter.html [6]: https://peps.python.org/pep-0020/ [7]: https://lkml.iu.edu/hypermail/linux/kernel/1510.3/02866.html [8]: https://cs.stanford.edu/people/eroberts/courses/cs181/projects/2010-11/WorseIsBetter/index.php/Worse-is-better.html [9]: https://en.wikipedia.org/wiki/Maneuvering_Characteristics_Augmentation_System#Scrutiny [10]: https://joeyoder.com/PDFs/mud.pdf [11]: http://www.laputan.org/ [12]: https://joeyoder.com/ [13]: https://en.wikipedia.org/wiki/Unix_philosophy [14]: https://en.wikipedia.org/wiki/Agile_software_development#History [15]: https://agilemanifesto.org/ [16]: https://agilemanifesto.org/principles.html [17]: https://en.wikipedia.org/wiki/Agile_software_development#Code_vs._documentation [18]: https://hbr.org/2021/04/have-we-taken-agile-too-far [19]: https://www.amazon.com/stores/author/B08FRTFQD8/about [20]: https://www.amazon.com/stores/author/B08HW23WY6/about [21]: https://www.amazon.com/Working-Backwards-Insights-Stories-Secrets/dp/1250267595 [22]: https://wiki.c2.com/ [23]: https://blog.codinghorror.com/the-broken-window-theory/ [24]: https://refactoring.guru/design-patterns/book [25]: https://refactoring.guru/design-patterns [26]: https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to [27]: https://programmingisterrible.com/post/173883533613/code-to-debug [28]: https://programmingisterrible.com/post/176657481103/repeat-yourself-do-more-than-one-thing-and [29]: https://mastodon.social/@tef [30]: https://en.wikipedia.org/wiki/Don't_repeat_yourself [31]: https://en.wikipedia.org/wiki/Don't_repeat_yourself#AHA [32]: https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction [33]: https://www.agileconnection.com/interview/worse-better-revisited-interview-kevlin-henney [34]: https://www.linkedin.com/in/abdallah-dorra-2bbba229/