summaryrefslogtreecommitdiffstats
path: root/content/blog/slowftware-development.md
blob: 5367798b0016b9d70667c94d3ee5539b6e31c0ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
---
title: Slowftware Development
date: 2024-09-30
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." <br>
> --- _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 needed to code that
solution.

The first factor, "previous experience," isn't controllable. (at least not for this post's purposes).<br>
The last factor, "time to code," is the least important, it can be adjusted (with hiring) or planning costs for the
estimated time required.<br>
The other two factors are the most influential. And the ones that are affecting how correct the "time to code"
estimation will be.<br>
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).<br>

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**.<br>
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 and testing. One example is:

> 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].

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.<br>
> ...<br>
> 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.

Although the philosophy itself advocates for better, a variation of it was adopted early on by Facebook
and attributed to its founder, Mark Zuckerberg:<br>
"Move fast and break things and the idea was if unless you're breaking some stuff, you aren't moving fast enough". They
used to build software as quickly as possible, even sacrificing correctness for the sake of a quick release.<br>
Another variation was used to justify releasing an inferior technology or a product sooner rather than a better one
later.

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 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,<br>
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.<br>
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.<br>
> ...<br>
> 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 previous forces mentioned, 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.<br>
> --- 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.<br>
> ...<br>
> 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.<br>
> ...<br>
> Instead of clean, we want boring code where change is obvious.<br>
> ...<br>
> 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.<br>
> ...<br>
> **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.**<br>
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-.<br>
Apply those ideas to make both the development environment and output products better, and more sustainable.<br>
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".


[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