Uncle Bob and Silver Bullets (2017)
hillelwayne.com
hillelwayne.com
Personally, I find that the people whose advice I love to read are guys like John Carmack or Jonathon Blow or Casey Muratori or Tim Sweeney. Those are guys that have or are continuing to solve difficult problems and have tons of hard won practical advice. When I look at someone like Uncle Bob or Ron Jeffries, I see people that struggle to write sudoku solvers and are mostly famous for giving advice, not for building great software.
I love taking advice from both groups, treating neither as a silver bullet.
And honestly, reading "Uncle Bob's" advice, I find a lot of it is outright bad (here's a good breakdown: https://qntm.org/clean ), or specific to Java's quirks, or has no actual backing other than that Bob think's it's a good idea.
I never treat his proposals as a silver bullet, but a great source of inspiraton. I wish there were more people teaching those things.
That's so funny.
Especially when someone like Uncle Bob says: "The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. This is not an assertion that I can justify. I can’t provide any references to research that shows that very small functions are better. What I can tell you is that for nearly four decades I have written functions of all different sizes."
Well, alright, in that context he's asking us to just take his word for it, but there are no tangible arguments here. Whereas Casey Muratori has a much more thoughtful exploration of this topic ( https://caseymuratori.com/blog_0015 ), and he's also written some very excellent code (in terms of solving difficult problems and doing useful things)
Some Talkers peddle opinions because they haven't got any experience. Most Doers peddle the experiences they had. Why does this matter? Anything can be opinion if it isn't backed by data. Many opinions that look great on paper are terrible in practice. If someone is offering you an experience (or anecdote), at least they have a single data point to back it up. A surprising amount of influence is exerted on the internet based on "one guy's opinion".
The better Talkers aggregate experiences of others to back up their opinions. This tends to provide better evidence than a single anecdote, and can be done even if you have no hands on experience with the subject.
So to your point a better way to frame it might be "what data/evidence is this person giving to support their argument?"
Why is that, do you think?
If I work in isolation from the users, don't have external requirements, don't care about future versions of the software - sure, their advice might be useful to me.
This isn't necessarily true, a lot of code can and is reused between games (math, physics, audio, etc.)
In terms of isolation from the users, that isn't really true either -- the users are the rest of your team. You have to build tools for the team to use and they better be at least somewhat usable, and you need to have something workable quick so you're not blocking your artists and level designers, etc.
To use a games example, the game programming patterns gentleman is most famous for his book, not for his game engines - yet the book is extraordinary and anyone would be poorer for skipping it
Uncle bob has clearly articulated a number of ideas I’ve found useful over the years, and so what if he also said some things we don’t agree with? He’s human
That said, the opinions of accomplished people like carmack and blow are definitely worth listening to. They are eloquent and have very interesting opinions
Having a talent to solve problems does not mean you can teach them. Many times the opposite is true, people that do not have great talent but a good self reflecting and observing and analysis capacity can extract the insights and general rules of a give discipline.
The former plus a good experience on the field is best source o advice, and to my knowledge Uncle bob have both.
Instead if should return an error "Floating points should be compared using this library function that includes acceptable difference. If you want exact math use BigDecimal or similar. If you know what you're doing use library function with acceptable difference = 0.0"
And yes, Uncle Bob is giving some terrible advice. My least favorite is his advice to split "too long" pure functions into a stateful class with "short enough" methods that later can be called in the wrong order because now there's a right and wrong way to call them.
I'm thankful I'm not the only one with this critique. I see this so often and it makes navigating and/or debugging the code a nightmare, while I hop around these tiny single use methods. A long function I can read top to bottom is a lot easier to grok!
There were people who abhorred the sight of non-stateful code, there was little opportunity to fiddle with objects in it, not realizing that they just made their and everyone else's job harder with more moving parts, a concept that, despite being very easy to understand and with analogues to physical machines, was lost on many programmers.
It is a constant discussion. You can always find examples of long functions with lots of state which is hard to follow and then there are always examples of code where people went too short. And then there are always people taking any of those examples and showing why they were wrong.
I myself like going to the extremes for fun/toy/throwaway projects (not only on these but also with rules like "no `if`" or "no raw `for` loop" etc) to try out all the different alternatives and then resetting my default based on cases where it went well and where it went bad.
No such rule is always right, but most hmstem from being exposed to too much of one extreme.
Tons of tiny functions, each doing almost nothing, frantically calling each other, and somehow useful functionality results.
Each function is very easy to understand, but how they together do useful tasks is near incomprehensible.
It's amazing to me how even smart people can be taken in by Harold Hill types. Can I get a job going around giving advice for $100k a pop? I promise to do better than that guy haha.
The team treated that as puzzle-solving, and it was part of their culture to tackle those kinds of challenges. There was light hazing of newbies whenever the CI didn't pass because methods were too big. So, that's why at least in this case. There was no technical argument, just inertia.
Code quality was atrocious. Things that could be 10 lines were 30 or 40, and extremely stateful.
Ousterhout is saying that we really want deep classes, i.e. the complexity of the implementation a construct hides should be considerably larger than the complexity it takes to use it. Which, at least to me, basically says "search for good abstractions". And what you're describing strikes me as a good way not to achieve that.
Of course, there's considerable wiggle room in that mental model, and one could still endlessly squabble about how (not) to break up the implementation.
[1]: https://www.amazon.com/Philosophy-Software-Design-John-Ouste...
I think the fallacy of the "Poltergeist Pattern" is to optimize solely on the - in itself valuable - metric of short simple methods, without consider anything else, like the complexity of using or reading the resulting code.
He says to turn the function into a class and local variables into private fields. So that you can split the function into small methods that operate on these private fields. I used it a few times in a company that had strict "no Sonar warnings" policy. But I hate the resulting code much more than the initial code.
Using it on the results of floating point arithmetic may be a bad idea, but that's a different matter. That should perhaps be a compiler or linter warning, rather than flat out be declared incorrect.
Here is some cursed C code to play with:
#include <stdio.h>
#include <float.h>
#include <math.h>
int main() {
int a = 0x1000002;
int b = 0x2000001;
float* fa = (float*)&a;
float* fb = (float*)&b;
printf("value-equals? %d\n", *fa == *fb);
printf("fabs-equals? %d\n", fabs(*fa-*fb)<FLT_EPSILON);
}
May print something like value-equals? 0
fabs-equals? 1I get the same output using the following (I switched to the portable memcpy solution.)
#include <stdio.h>
#include <float.h>
#include <math.h>
int main() {
int a = 0x361dc5df; // 2.351e-6f
int b = 0x361e3e22; // 2.358e-6f;
float fa, fb;
memcpy(&fa, &a, 4);
memcpy(&fb, &b, 4);
printf("value-equals? %d\n", fa == fb);
printf("fabs-equals? %d\n", fabs(fa-fb)<FLT_EPSILON);
}
Since FLT_EPSILON is 1.19209290E-07F , I would expect a lot of small, close numbers to give the same output, even without touching subnormal values.In this context, Uncle Bob's style was compelling, it felt like a breath of fresh air. His opinionated attitude was also compelling. In general, associating a term like 'clean' to your methodology seems to be a good zealot recruitment strategy. 'Pure functions' is similarly brilliant marketing.
That said, a lot of his supposed solutions actually cause problems of their own if you let the pendulum swing too far. It creates its own kind of complexity. Books like these also tend to sort of get their own momentum, if you look back you see a lot of big names praising them. Surely they must be good, right? Well they were. Clean Code made sense when it was published. It's for the most part not good advice today.
*Edit: Updated: I had forgotten this was not from "Clean Code".
They don't have to (and probably shouldn't) be globally defined.
Uncle Bob is a fraud by the way. Turning a perfectly readable medium length function into a class with dozens of tiny methods is a cardinal sin in my eyes. Then later someone stumbles upon this class and reuses some of these methods in one way or another, while working on a problem completely unrelated to the original, thus entangling the two implementations…
you may not be an adherent of judo and you may prefer and better understand karate, but that doesn’t make a judo teacher a fraud.
What's the programming analog of UFC/MMA?
The analogy also doesn't work because BJJ isn't some silver bullet. What people discovered is that the first M in MMA is actually the important part and if all you know is BJJ you're going to get starched by a boxer with a sprawl, or more likely a wrestler with a modicum of submission knowledge, who will never let you get to the floor in the first place, and instead just grind you out.
So just like the question "what is the best martial art" currently has no answer outside of "you need a mix of striking and grappling not just one thing", there is no answer to "What is the best programming style" outside of "think about the problem you have at hand and crib on examples and knowledge from other people who have solved a similar problem". This "unfortunately" points to boring industry standard tools, like Java, C/C++, Javascript, RDBMSs, IDEs, Linux etc, etc. Probably some newer stuff like Rust and React as well. And note that answer isn't one specific technology, like MMA its a bag of different tools you combine.
assertEquals(expectedOutput, f(input));
When you split them in a way that makes the local state external - you now have to handle the state in unit tests which is much more hassle.BTW I'd also love to have a built in float type that fails when you assign 0 to it.
Anyway, I guess in Java operator == is a lost cause anyway. My favorite example:
// true
System.out.println(BigDecimal.valueOf(10) == BigDecimal.valueOf(10));
// false
System.out.println(BigDecimal.valueOf(11) == BigDecimal.valueOf(11));
// true
System.out.println(BigDecimal.valueOf(11).equals(BigDecimal.valueOf(11)));Yeah, but then you're having to learn all the special cases for when it silently gives wrong answers, and hope to hell that you didn't miss any.
Much better to have consistency and behave the same way all the time, than to optimise for 3 keystrokes and introduce all sorts of special exceptions that the programmer must memorise.
Then people would start writing `signum(a - b) == 0` (or some equivalent) instead of `a == b`, and/or factor that out into a helper function. Not sure that would be an improvement.
I fixed two bugs at once in a softsynth caused by this stupidity. In the envelope generator code, which loosely modelled the ADSR circuit in a synth which charges and discharges a capacitor, there was an integrator implemented like `env = ((target - env) * timeconstant) + env`. The `target` value was set to 1 to allow the "capacitor" to "charge" at a rate set by `timeconstant`, and when it reached the top it would be flipped to 0 and `timeconstant` changed to the decay rate.
It failed about one go in ten, with some fudges to set `timeconstant` in odd values. It turns out that they detected the "capacitor fully charged" state with something like `if (env == 0.999) { dostuff(); }` and for some values of `env` and `timeconstant` it might never hit *exactly* 0.999 if you got it exactly wrong.
It was logically wrong (detect a precise float value) and functionally wrong (envelopes switch over when the cap is about 2/3 charged). Changing the "flip over" condition to something more like `if (env > 0.999)` and changing the maximum value of `target` to 1.5 (mimicking a circuit running off 15V and trying to it a 10V output, like a real analogue synth) cured all its problems.
Well, all its *envelope* problems. There were more, but they get into scary discussions of poles and zeroes and negative frequencies, and you don't want that at this time of night.
His dogmatic approach is partially, from what I've seen, a counterbalance to the sloppiness that a large part of the industry, especially corporate America, has with software development. Can you take it too far and create beautiful software that does not solve any problems? Sure, but on the flip side it is also easy to create a lot of technical debt.
With things like TDD, when devs are new to it, part of the learning process is to take it too far first and then as you understand it you learn the right balance.
Depending on the industry, reliability and ability to ship software that functions as advertised from day #1 can be a huge advantage, and for others it can be a wash or even a competitive disadvantage if it consumes too many resources. But I would not dismiss any such tool out of hand just because you personally have not seen or heard about the success stories.
After two decades of consulting in the software industry, you belong to a certain professional network. People love to talk, even if they won't always name their customers. I know people using formal methods and verification, in niches of the industry really, but have no indication that TDD is utilized for any big flagship products where it really matters.
I'm sure there are a few highly blogged about industry stories somewhere, but there's a world of a difference between those and a long time success in software development as a whole. There were high profile books written about Rational Unified Process, for example, and ~ nobody uses that anymore.
https://github.com/linux-test-project/ltp/wiki https://github.com/neovim/neovim/tree/master/test https://github.com/openssl/openssl/blob/master/test/README.s...
What are you trying to say, that people don't write tests for their code?
Test-Driven Development means that the tests are written before the features.
OK, write the test…
> write a few lines of production code to make it pass
…before the feature.
I recognize the color you’re trying to add (it’s more like TFTFTF than TTTFFF), but it remains Test-Driven Development.
You're conflating unit tests with general testing
Uncle Bob makes it very clear that he only considers "unit tests" to be valid
So, no, these dogmatically "don't count"
(they do count when Uncle Bob supporters want to play half-truths and push for TDD instead of more realistic tests)
They obviously produce useful results and most would love to see more of it, yet it is tacked on afterwards and often by completely different people than the original developers, not as part of the development process.
They have a long way to go to have good enough tests that could be relied on for integration purposes. TDD isn't even on the map. And will probably never be.
Do you expect it to be advertised?
If nothing else, apparently there are enough of disciples out there that it would be a great way of hiring the right people.
You'd have to believe there is some sort of cabal, completely bent on keeping this enormous advantage to themselves in absolute secrecy, to argue that it is a software development success story.
One can’t recommend his books with good conscience to anyone who doesn’t already have a very good judgment regarding software design and can pick out the parts that actually make sense.
Calling bound functions ”methods” is perhaps a bit misleading.
It is funny how much of these types of works/teachings exist across a lot of domains. Writing fiction you'll see similar people claiming "this is the ONLY way to write a novel," which is patently stupid, but they'll claim it all the same, using weird logic to cram all kinds of great works into their model even when they don't really fit.
I worked with programmers who worshipped at Uncle Bob's feet and never seemed to notice they never produced functional systems by any stretch of the definition.
They then adopted Bob's dismissive attitude about everything, you see, because they already had the answer to all the problems: just smurf -- I mean unit test -- it!
> Uncle Bob is okay with software correctness: after all, he uses the phrase “unit testing” like a smurf uses “smurf”. But what about every other correctness technique? (but for Uncle Bob...) any correctness technique that isn’t unit tests can be dismissed.
If I had enough time, I'd write perfect software but mid writing perfect software my company needs the following 3 features or they go under.
Sure if I was working at Google I could spend the time making perfect stuff and after 3 years finally get that customer engagement up by 0.1% but working in startups we're often not given the luxury of time, or sometimes experimentation. We make decisions based on our current skills/knowledge/time constraints, and then we have to live with those decisions. I have to ramp up engineers with 1-5 years of experience who don't know all the patterns and sometimes are even learning the language we're working in, and they make mistakes.
Point is... from my experience... Good software is written when strong engineers at the top know how to train weaker engineers on the bottom, and together work towards a common vision. Struggle together, win together, fix the warts.
> Plan for a world where we are just as stupid tomorrow as we are today.
If an outage or issue was caused by a mistake, the solution can never be “don’t make that mistake again.” People don’t choose to make mistakes, so they can’t choose to not make them, either.
The trouble is he delivers that terrible 10% with equal confidence and it really undoes all the good advice.
Uncle Bob has influenced me during my career, but I don't nor do I know any other senior developer who preaches his gospel. Most people just pick up the bits and pieces they like and forget about the rest.
Maybe you'll get some juniors who get overly zealous but I think there's worse things a junior could be doing than following Bob a little too strictly.
Ruby has long been my favorite programming language and I used to believe that the static typing of languages like Java were the main reason they were so much less effective. After having worked with Typescript for a couple years and seeing the magic the Rust community is pulling off I now believe we'd benefit from static types in Ruby, as do an increasing amount of Rubyists.
The true danger is getting stuck in your ideas, and ignoring the wisdom that's being developed around you.
Lucky for you I guess. Ive known a few. "Clean code" is also still a pretty commonly recommended book.
People do cite him in PR arguments as a kind of appeal to authority and thats kind of unfortunate.
Uncle Bob and Silver Bullets (2017) - https://news.ycombinator.com/item?id=26153823 - Feb 2021 (92 comments)
Uncle Bob and Silver Bullets - https://news.ycombinator.com/item?id=15415278 - Oct 2017 (218 comments)
And in fact we have been doing it - the tools are so much better than they used to be and with tdd, refactoring, patternals and clean code we have been building increasingly complex software with less bugs.
Still a long way to go.
Quoting from “tools are not the answer” (http://blog.cleancoder.com/uncle-bob/2017/10/04/CodeIsNotThe...)
> The author of the article interviewed many thought leaders in the industry, but chose only those thought leaders who were inventing new technologies. Those technologies were things like Light Table, Model Driven Engineering, and TLA+.
> I have nothing against tools like this. I’ve even contributed money to the Light Table project. I think that good software tools make it easier to write good software. However, tools are not the answer to the “Apocalypse”.
> Nowhere in the article did the author examine the possibility that programmers are generally undisciplined. The author did not interview software experts like Kent Beck, or Ward Cunningham, or Martin Fowler. The author completely avoided the notion that the software apocalypse might be avoided if programmers just did a better job. Instead, the author seemed convinced that the solution to the software apocalypse – the solution to bad code – is more code.
> I disagree. Tools are fine; but the solution to the software apocalypse is not more tools. The solution is better programming discipline.
If you step back for a second, this is basically the main gripe you see repeated on HN; business/PM is always in a hurry, the pressure is always to cut corners, nobody has time to build things right/properly.
His point is that in this environment (most software environments) just sprinkling some TLA+ in there is not going to solve the problem. If your PM is always rushing you can you imagine them letting you pause on delivering features to prove your system is correct? Most shops do not care enough to justify this expense.
I think by phrasing it as discipline Bob makes it sound like it’s the fault of individuals, where in fact I think a lot of people would like to spend more time on quality but just don’t have org buy-in. (But there are sloppy/undisciplined individuals too.)
I tried using "fast-check" for some parsing code in JavaScript that needs to handle floats and it was pretty good at reminding me of the corner cases. Or at least some of them.