Skip to content

Commit

Permalink
Small grammatical fixes
Browse files Browse the repository at this point in the history
Great article!
  • Loading branch information
ehershey authored Jan 12, 2021
1 parent cf373b8 commit cd45db1
Showing 1 changed file with 6 additions and 6 deletions.
12 changes: 6 additions & 6 deletions content/articles/small-sharp-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@ More background for the idea is described under a section on Unix philosophy tit

> One strain of Unix thinking emphasizes small sharp tools, starting designs from zero, and interfaces that are simple and consistent. This point of view has been most famously championed by Doug McIlroy. Another strain emphasizes doing simple implementations that work, and that ship quickly, even if the methods are brute-force and some edge cases have to be punted. Ken Thompson’s code and his maxims about programming have often seemed to lean in this direction.
One example of the Unix-based small, sharp tools that are being referred to here are the basic shell primitives like `cd`, `ls`, `cat`, `grep`, and `tail`, which can be composed in the context of with pipelines, redirections, the shell, and the file system itself to work in tandem to build more complex workflows than any could provide on their own. The "simple and consistent" interfaces are the Unix conventions such as common idioms between programs for specifying input to consume, and [exit codes](/exit-status) that are re-used between programs.
One example of the Unix-based small, sharp tools that are being referred to here are the basic shell primitives like `cd`, `ls`, `cat`, `grep`, and `tail`, which can be composed in the context of pipelines, redirections, the shell, and the file system itself to work in tandem to build more complex workflows than any could provide on their own. The "simple and consistent" interfaces are the Unix conventions such as common idioms between programs for specifying input to consume, and [exit codes](/exit-status) that are re-used between programs.

Notably, it's never claimed that this is a fundamental of value of Unix, but more one of the competing philosophies that was baked into the system in its early history.
Notably, it's never claimed that this is a fundamental value of Unix, but more one of the competing philosophies that was baked into the system in its early history.

## The web (#the-web)

The idea can be applied to the web in a similar way. Many modern web applications are choosing to build out their architecture as a set of services that communicate over the network layer, which has some parallels with the Unix model of programs that communicate via OS primitives.

This may be especially application to today with the recent popularization of [microservices](/microservices), a philosophy that advises building web architecture as a set of services that are kept small so that they can be easily reasoned about, operated, and evolved (also known as SOA).
This may be especially applicable to today with the recent popularization of [microservices](/microservices), a philosophy that advises building web architecture as a set of services that are kept small so that they can be easily reasoned about, operated, and evolved (also known as SOA).

## The catch (#the-catch)

Unfortunately, rather than being a solution that's perfectly applicable to all problems, the trade-offs small, sharp tools are fairly well understood. In a section discussing the "compromise between the minimalism of ed and the all-singing-all-dancing comprehensiveness of Emacs", Raymond talks about how building tools that too small can result in an increased burden on their users:
Unfortunately, rather than being a solution that's perfectly applicable to all problems, the trade-offs of small, sharp tools are fairly well understood. In a section discussing the "compromise between the minimalism of ed and the all-singing-all-dancing comprehensiveness of Emacs", Raymond talks about how building tools that are too small can result in an increased burden on their users:

> Then the religion of “small, sharp tools”, the pressure to keep interface complexity and codebase size down, may lead right to a manularity trap — the user has to maintain all the shared context himself, because the tools won’t do it for him.
He continues with another passage on the subject under the section titled _The Right Size of Software_:

> Small, sharp tools in the Unix style have trouble sharing data, unless they live inside a framework that makes communication among them easy. Emacs is such a framework, and unified management of shared context is what the optional complexity of Emacs is buying.
Once again, it's incredible how well this carries over to building modern service-oriented systems. Consider for instance two small programs that handle account management and billing. Although each is small and sharp in its own right with concise areas of responsibility and boundaries, an operator may find that even if each is well-encapsulated, complexity may arise from the shared context between the programs rather than either program in its own right. Where a monolithic system might be able guarantee data integrity and consistency by virtue of having a single ACID data store, two separate programs suddenly have to consider overhead like drifts between data sets, and the acknowledged complexity of distributed transactions.
Once again, it's incredible how well this carries over to building modern service-oriented systems. Consider for instance two small programs that handle account management and billing. Although each is small and sharp in its own right with concise areas of responsibility and boundaries, an operator may find that even if each is well-encapsulated, complexity may arise from the shared context between the programs rather than either program in its own right. Where a monolithic system might be able to guarantee data integrity and consistency by virtue of having a single ACID data store, two separate programs suddenly have to consider overhead like drifts between data sets, and the acknowledged complexity of distributed transactions.

I'm reminded of this pitfall almost every week. Just a few days ago, it turned out that an internal identifier generated by our billing component and which had previously seen only restricted use was now expected to be more broadly available. Backfilling the missing data would require a time-consuming manual operation.

Expand All @@ -49,7 +49,7 @@ As a general rule to help select tool boundaries, Raymond goes on to suggest the

> Choose the shared context you want to manage, and build your programs as small as those boundaries will allow. This is “as simple as possible, but no simpler”, but it focuses attention on the choice of shared context. It applies not just to frameworks, but to applications and program systems.
We recently got this wrong. We wanted to build a system that improved the experience of managing a shared app by inventing the concept of an organization that would own an app and grant certain permissions to developers who wanted to collaborate on it. It was implemented a separate service that would use the existing Heroku API to transparently enrich the end user's experience. Although initially regarded as a very positive concept, we soon observed that the surface area of the private API's required for the new service swelled to an unmanageable size and the shallow integration made it difficult to implement new features without extensive changes across both components and their communication surface. The decision was made to re-integrate it.
We recently got this wrong. We wanted to build a system that improved the experience of managing a shared app by inventing the concept of an organization that would own an app and grant certain permissions to developers who wanted to collaborate on it. It was implemented as a separate service that would use the existing Heroku API to transparently enrich the end user's experience. Although initially regarded as a very positive concept, we soon observed that the surface area of the private API's required for the new service swelled to an unmanageable size and the shallow integration made it difficult to implement new features without extensive changes across both components and their communication surface. The decision was made to re-integrate it.

The mistake we made was that although each service was kept fairly simple with the separation, the sum of their complexity was much greater than that of the integrated system. Despite being less sound from an idealogical standpoint, the monolith could be improved more quickly, operated more easily, and could better guarantee data consistency.

Expand Down

0 comments on commit cd45db1

Please sign in to comment.