
My favorite principle for code quality - spenny2112
http://www.pathsensitive.com/2018/02/making-bugs-impossible-illustrating.html
======
mikekchar
My advice for design: Design is hard. Good design is so hard that you will
almost never see it in the wild. It becomes even harder when you work with
other people. Your carefully crafted ideas will be crushed under the foot of
the next confident programmer who thinks they have found the holy grail of
design. As more and more programmers are added, they pour in their particular
favourite flavour of programming sauce. The result is swamp water.

Good design is not about the code. Good design is about the people who are
going to work with that code. Understand your audience. Get to know what they
like and what they don't like. Write your code in a way that will delight your
audience. Give them what they expect to see.

Of course, this is exceptionally shallow advice because no programmer can
actually be so selfless. If you write code that pleases your audience you will
usually have to do so at the expense of yourself. It is not possible to write
good code that you dislike -- at least not long term.

So the trick to good design is always putting a bit of yourself in the code
and always putting a bit of your colleagues in the code. It's striking that
balance where you are satisfied, but also where they are satisfied. This
requires working in small increments, and sharing what you are doing. It
requires watching what your colleagues are doing and looking for opportunities
to borrow. It requires questioning, evaluating, praising, explaining and
coaching.

And if you are working by yourself: do whatever the hell you want. Why are you
so worried about what other people think? If you are trying to improve what
you are doing, don't do so by reading about the opinions of others. Read code,
not blog posts. Write code, not design diagrams. Work slowly, try out ideas
you've seen in other code and evaluate their success. When you get the chance,
work with others to broaden your horizons.

~~~
sooheon
I like the way you think, do you write anywhere else?

I'm only in my first year of designing and writing code professionally, much
of what you point out about this being 1. difficult, and 2. a social/human
endeavor rings true.

~~~
mikekchar
I have promised to, but have not followed through with that promise. One of
these days maybe...

------
antirez
I can't disagree more with this post. One should always aim at trivial code,
but as things get more complex, it is there the the sensibility should fire in
and make you say, ok I need more abstraction now, and refactor accordingly.
But even then it is important to just add the minimum amount of abstraction to
generalize the problem at hand, without thinking like "but why if in the
future we change things..." Unless it's very clearly going to happen soon.

~~~
auxbuss
There's an excellent discussion on this topic in the sample chapter,
Rediscovering Simplicity[0], from Sandi Metz's book, 99 Bottles of OOP[1]. I
often use it as a source for discussion with devs.

[0]
[https://www.sandimetz.com/99bottles/sample](https://www.sandimetz.com/99bottles/sample)

[1] [https://www.sandimetz.com/99bottles](https://www.sandimetz.com/99bottles)

~~~
ScottBurson
I like this, though I think anyone who mentions SLoC as a metric is honor-
bound to quote Dijkstra: _My point today is that, if we wish to count lines of
code, we should not regard them as “lines produced” but as “lines spent”: the
current conventional wisdom is so foolish as to book that count on the wrong
side of the ledger._

------
vorpalhex
> Standard disclaimer: When reading software design advice, always imagine the
> examples given are 10x longer. Overengineering is bad. But, if you’re not
> sure whether applying a technique to your code would be overengineering,
> error on the side of doing it. Abstract early.

Please, please, please absolutely disregard this advice. More errors, pain and
suffering come from early abstraction and poor understanding then not enough
abstraction.

Build simple, extend as needed. It's a 'frggin stats page where a developer
forgot to remove a call. It could of been written much cleaner initially, but
breaking it into a bunch of classes at the initial stage is fruitless
abstraction.

~~~
sharpercoder
This boils down to _code removeability_. A few larger classes wit large
methods is in general easier to remove/refactor then a large amount of small
cohesive classes with an obtuse design concept behind it. Of all the codebases
I refactored, I take the "large methods and large classes" hands down. Pulling
a few classes here and there is easy, compared to first understanding a
clusterf*ck of overengineered abstractions.

~~~
ljm
I worked with a domain driven setup at one point, which seemed like it was
designed to sell JetBrains licenses because it became almost unbearable to try
and maintain the codebase with a text editor. You would have to go through a
controller, a DI container, a repository, an entity, a factory, a builder,
maybe a facade, and an event bus... almost all of which were single-method
classes (except for the DI boilerplate which was split between constructors
and YAML files) that just called the method of the next dependent class. One
line of concrete business logic hidden between half a dozen files full of
architecture.

The rationale was that the abstraction was necessary to make things easier to
replace if they weren't needed but it was a false assertion on two levels (and
it almost always is):

\- that kind of replacement is unlikely to happen in the short/mid-term, and
if it does it won't be in a way you anticipated.

\- the simple version could be deleted and rewritten in less time than it
would take to fix your highly abstracted/decoupled/meticulously architected
integration

Of course, simple isn't easy and this approach to abstraction (where you take
classes/methods longer than _x_ lines and extract them into more classes and
methods) is very easy to achieve... at a great cost.

~~~
greggyb
This makes me think of a good thinking talk, with a quote I pull out from time
to time:

> I hate code, and I want as little of it as possible in our product.

[http://pyvideo.org/pycon-us-2012/stop-writing-
classes.html](http://pyvideo.org/pycon-us-2012/stop-writing-classes.html)

~~~
milesvp
I was always disappointed I couldn't find more talks by Jack Diederich after
watching this one. His approach was so practical compared to so much
programming advice out there. I see now that he's got a few more talks linked
from here.

I know how I'll be spending a few hours in the next few days. Thank you for
reminding me of his talk!

------
ljm
I love articles like these because every time I'm naïvely expecting to learn
something new, but more often than not I'm left unsatisfied.

It's always the same pattern and it's like the programmer's version of the one
weird trick. Show ten lines of (contrived) code and talk about its potential
to be flawed, and then 'fix' it by explaining 50+ lines of code that has to be
abbreviated or split into pieces to keep the article flowing. What quality
actually means in this context is unclear, but it feels like tidying your
bedroom as a child: hide the clutter in various places to give the appearance
of cleanliness, while actually creating more of a mess for the next cleanup in
the process.

And like many programming maxims, advising to abstract early is just as bad as
advising to not abstract at all. Better abstraction will become more apparent
as you see how your system fits together and how parts of it can be lifted up
to a higher level, and this kind of intuition will only come with patience and
experience.

------
jpochtar
I believe the code presented would have benefited from a lambda-based API
approach instead of an object-oriented one. In Javascript:

    
    
      // global, since the cached values were apparently global in the original
      let cache = {};
      let lastCachedTime = 0;
    
      // Utility for fetching potentially cached values, or computing them 
      // if there's no valid cache entry.
      let cached = (id, recompute) => {
        // I don't love this policy, but thankfully it's all in one place.  
        if (lastCachedTime <= lastMidnight() || lastCachedTime <= lastNoon() {
          cache = {};
          lastCachedTime = Time.now();
        }
        if (!(id in cache)) {
          cache[id] = recompute();
        }
        return cache[id];
      };
    
      // now we get to have a nicely factored, simple, conceptually independent, 
      // declarative code for the dashboard.
      function displayDashboard() {
        print("Total Users: " + cached("numContent", => countUsers());
        print("Articles written: " + cached("numArticles", => countArticles());
        print("Words written: " + cached("numWords", => countWords());
      }
    

This achieves the same "Embedded Design" goal. If you squint, it's actually
virtually the same as the DashboardStatComputation/DashboardStat/Dashboard the
author comes up with, where each lambda in the displayDashboard function is
corresponds to a DashboardStatComputation.

I prefer this lambda-based design because it reifies the "form" into a single,
callable, function: _cached()_. If you want to use the idea of _cached()_ ,
you don't have to subclass anything or know about its implementation details—
just call it.

(PS. the code's even nicer in CoffeeScript)

~~~
zackbloom
Yes, 90% of abstraction is best done by organizing files and functions, NOT by
creating classes.

~~~
mark_edward
Unfortunately these are largely the same thing if you're stuck with Java <= 8

------
everdev
> The Embedded Design principle, which I briefly introduced in a previous
> post, states that you should always code in a way that makes the design
> apparent. It’s like pixie dust in its ability to make code more readable,
> loosely coupled, and impervious to bugs.

I'm all for good software design, but claiming a design process can eliminate
bugs is more fantasy than reality. Bugs typically come from unforeseen
requirements or application states.

Yes, planning reduces bugs, but bugs are still inevitable no matter what
software design process we use.

~~~
codemac
Cleanroom has a lot of evidence that process (especially around code
inspection/review) does improve quality more than TDD or other methods of
detailed design, most people are just unwilling to take the time for detailed
process for quality IMO.

~~~
crdoconnor
That's frequently a reasonable trade off. A lot of businesses need a proof of
concept to test product market fit. IMHO TDD can often be a massive waste of
time if that is the case.

There's weirdly not a lot of good advice out there on sensible steps to
reasonably transform a rushed proof of concept in to a production quality app.
Mostly it all starts with the assumption that you're building something from
scratch.

~~~
codemac
> There's weirdly not a lot of good advice out there on sensible steps to
> reasonably transform a rushed proof of concept in to a production quality
> app. Mostly it all starts with the assumption that you're building something
> from scratch.

Cleanroom is actually completely reasonable for this - what you must accept is
that transforming a rushed proof concept into a production quality application
_is_ starting from scratch in terms of ascii in github. It's the experience of
those that worked on it that is valuable to save.

------
sbov
> “There’s a bug over there because you re-used lastCachedTime. Otherwise,
> looks good. Ship it!”

The fixed code still has a bug. It assumes successive calls to lastMidnight()
within the method will always return the same date. But presumably, if partway
through the method it ticks over to midnight, that would not be the case. So
some stats would be updated during one call, and the rest would be updated
during the next call.

That would violate this requirement:

> They had previously decided all stats should refresh simultaneously;

~~~
lnanek2
He does point out every good programer knows they should have one if check for
all three: " Wrapping Up . Many programmers will get a sense that something
was off about the first example. Every programmer I’ve shown this example to
knew they should merge the if-statements and factor out the prints. "

Which corresponds to the one if part of the story: " if (lastCachedTime <=
lastMidnight() || lastCachedTime <= lastNoon()) { numUsers = countUsers();
numArticles = countArticles(); numWords = countWords(); lastCachedTime =
Time.now(); } "

So having the multiple ifs being bad even before a second cache refresh is
added is part of the story.

------
joslin01
I feel like this is a long-winded way of saying what Linus already has
famously said:

"Bad programmers worry about the code. Good programmers worry about data
structures and their relationships."

The author refactors some code to revolve around a "DashboardStat" and
explains it came from "thinking about how the code is derived from the
design." Personally, I think it came from putting the data & its structure
first.

~~~
gowld
[https://softwareengineering.stackexchange.com/questions/1631...](https://softwareengineering.stackexchange.com/questions/163185/torvalds-
quote-about-good-programmer)

gives some of the genealogy of the idea.

[http://www.faqs.org/docs/artu/](http://www.faqs.org/docs/artu/)

"Rule of Representation: Fold knowledge into data so program logic can be
stupid and robust."

[https://users.ece.utexas.edu/~adnan/pike.html](https://users.ece.utexas.edu/~adnan/pike.html)

"Rule 5. Data dominates. If you've chosen the right data structures and
organized things well, the algorithms will almost always be self-evident. Data
structures, not algorithms, are central to programming."

------
keithnz
This was a pretty bad article on achieving code quality.

First, there was a bit of bashing of design patterns and refactorings and says
we can forget all that in order to make it sound like he was going to drop
some magic fundamental wisdom. Which he then says is "Embedded Design
Principle".

Then he proceeds to describe an evolution of a piece of software into a bit of
a mess, then proceeds to solve it by "AND here is a design that solves it". No
method or process for getting to that design, just "here it is!" .... drop the
mic, exit stage left. So basically his advice is dont write crappy code, some
how magic up a design that might or might not be robust for the future that
reveals your design.

Looking at some of his other stuff, he uses a similar "strawman" ish approach
to first highlight some technique like (TDD), come up with some crappy code,
and use it as some kind of justification for what he thinks.

Funny thing is, the stuff he trivialized actually are exact micro techniques
that allow one to create code thats shows its design.

However, for this article, there is a simpler idea that is useful when
tackling code like this ( I first came across it in martin fowlers UML
Distilled ) and that was the idea of "Levels of Perspective". Which there is
Conceptual, Specification, and Implmentation. Since his code seems to have
purposely mushed all those things together, its an easy way to look at this
code and make it better. I won't go into the exact details, but given a piece
of crappy code either in the beginning stages or even the end stage and start
decomposing it, first seperating specification and implementation, then as one
gains insight, seperating things into composable conceptual ideas. To achieve
this, you want to use things like TDD, Refactoring, Patterns, but, and the
real trick, only as is demanded to shape the code into a simple design that is
modular and composable.

------
gHosts
Hmm.. Grrumble. I think he has the tail of something.... but hasn't stated it
well.

Personally I keep coming across code where the "design" as indicated by names
and comments indicate one thing.... but the fact of the code indicates another
design.

Now if this indicates a bug, ie. Incorrect behaviour, yup, fix it.

But usually it has been "tested into the shape of a product". ie. The names
and the comments and the data structures indicated first, draft, incorrekt,
thoughts... and the reality of the code _is_ the correct behavior.

Usually at that point I refactor to reveal what the design really is, down at
the mathematics of the code, not what the designer thought it was.

------
et1337
I prefer a more focused version of this principle: you should organize your
data in a way that makes the design apparent. The code will follow later.

This principle still works with the author's example. Most of the refactoring
he did boiled down to modeling the data more accurately.

~~~
lttlrck
Algorithms + Data Structures = Program

This was something that was stressed by my mentor, who was a mathematician
turned programmer and a big fan of Niklaus Wirth. It has proven valuable to me
on numerous occasions and informs design.

[https://en.m.wikipedia.org/wiki/Algorithms_%2B_Data_Structur...](https://en.m.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs)

------
dmytroi
The post indirectly suggest creating domain specific language for solving
dashboard presentation problem. In my opinion this shifts engineers focus to
solve problems with-in provided domain specific language, which in the end
resolves in job security, as non-senior engineers will not "dare" to touch
"the framework". Whole situation, in my opinion, results in "everything
working on paper" (aka tests, good design, etc) while in reality nothing works
and as project progresses engineers get a feeling that "nothing can be done".
I would just remove "numWords = countWords();" line and move on. There is no
need to invent domain specific language when just programming language is
sufficient. I would love engineers to be more reasonable and aware of DSL's
trade off.

------
sp332
_Robert Piersig illustrates these higher-level concepts, which he calls
forms,_

Pretty sure that's attributed to Plato. Well I guess he called them _είδε_ but
he couldn't wait around for English to get invented.

~~~
aero142
I think that was one of the great things about Zen. It was an unreliable
narrator who had megalomania. At one point, he acknowledges that most of what
he is stating is pulled from older greek philosophy that he didn't bother to
learn properly but then continues to claim to invent new philosophy through
the story.

~~~
SilasX
I get the feeling that most fans of _Zen_ didn't interpret the narrator as
unreliable :-/

------
kqr
I agree with the general sentiment, but suggest exercising caution: Every
problem can be solved by an additional layer of abstraction, except the
problem of too many layers of abstraction!

~~~
gitgud
Is that a quote from someone? It nicely sums up my naive experience of trying
to "clean up" my code

~~~
j_tomanik
[https://en.wikipedia.org/wiki/Fundamental_theorem_of_softwar...](https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering)

------
davidhyde
"Abstract early" is just plain bad advice because you usually don't know the
full problem up front. Better advice would be to write code that lends itself
resilient to refactoring (i.e. it is easy to refactor and won't break in
unexpected ways when you do so)

Additionally, the authors "better" design now abstracts the primary
performance problem out of sight of the developer. The main problem I see is
that they are making multiple passes through the data to extract different
metrics. By abstracting early they miss the opportunity to improve the
performance by calculating all the metrics in one pass. The new design has
hidden the root cause of the problem and prevented a developer from fixing the
core problem without chucking out the entire design.

------
erpellan
Ask yourself what assumptions you are baking into the design. Can you make
them more explicit? How likely are they to change? Where multiple approaches
are on the table, favour the ones with the fewest assumptions. Be especially
critical where cardinality is concerned. It's much easier to go from n to n+1
than from 1 to 2.

------
perpetualcrayon
I find it extremely disconcerting that so many software engineers don't follow
this. You're not there to figure out some clever way to do something that you
made up. You're first and foremost there to figure out the essence of the
thing itself and to find where the design exists naturally. Only then can you
really start to design things well IMO.

------
flukus
This is a very complicated solution for a problem the compiler would warn you
about (unused variable).

------
dsego
How does the final example solve the daily slowdowns?

------
formatkaka
Abstract Early? That's kind of Anti-KISS . It's better to start simple and
abstract when desired.

It wastes a lot of time when I try to abstract early. You can't solve a
problem without all the variables.

------
mlthoughts2018
I feel like there must be an equation such as:

 _Peter Principle + Embedded Design Principle = Conway 's Law_

------
pnj9
More often than not AwesomeSauce will end up SpectacularFailure, thus making
the exercise in divining the future use case of 30 lines of code pointless

------
trhway
seems the article ends up with what was first done decades ago in PHP -
separation of presentation and business logic. Btw i don't understand all this
animosity toward PHP, especially given that pretty much all the modern web
development is basically PHP-like, in spirit if not in actual implementation.

~~~
vorpalhex
> i don't understand all this animosity toward PHP

Most PHP code is _terrible_. That's not really PHPs fault - it's capable of
producing very nice programs with beautiful behavior.

As a language, it suffers a bit from being older and it was internally very
inconsistent for a long time. For a long while, it's package management and
practices were way behind the curve. Many people who program in PHP learned it
as a first language, hacked together programs in it, and probably used hack
together libraries and tooling.

PHP can be used with grace, but most people don't have that experience with
it.

~~~
yani
For what use case is PHP better than other languages?

~~~
jstewartmobile
Rapid development of performant get/post web interfaces. Ruby is nice but runs
a lot slower. Python is better in a general-purpose sense, but every file will
have a preamble of includes for things that are just built-in to PHP.

As a general purpose language, PHP is weak. As a web-form DSL, it's great!

