top | item 24834965

Discipline Doesn’t Scale

320 points| ingve | 5 years ago |sicpers.info

169 comments

order
[+] Animats|5 years ago|reply
Over a decade ago I used to argue this with the C++ committee people. Back then, they were not concerned about memory safety; they were off trying to do too much with templates. The C++ people didn't get serious about safety until Rust came along and started looking like a threat. Now they're trying to kludge Rust features into C++ by papering over the unsafe stuff with templates, with some success. But the rot underneath always comes through the wallpaper. Too many things still need raw pointers as input.

The encouraging thing today is that we're kind of converging on good practices in language design.

- You really want to declare types for function parameters. The compiler needs that information for checking, and the programmer, and the maintenance programmers who follow behind, need that information so they know how to call the thing. On the other hand, you can usually infer the result types of expressions by a simple forward process. So C++ gained "auto", Rust and Go started off with that arrangement, Javascript got TypeScript, and Python is getting there via a side trip through unchecked type declarations.

- The three big questions in C: "How big is it?", "Who can delete it?", and "What locks it?" C gives no help with any of those. C++ now tries to address the first two. All the garbage-collected languages address the first two. Rust addresses all three.

- The idea that locks should be connected by syntax with the data they are locking took far too long to become accepted. Partly because the Ada rendezvous and the Java synchronized object didn't work well. Even Go doesn't do that very well. But language design seems to be coming around. When and if thread-type concurrency comes to Javascript, the webcrap crowd will create race conditions everywhere without automation in locking.

[+] jchw|5 years ago|reply
I think this is a little oversimplified, because I’m pretty sure C++0x had move semantics and usable smart pointers prior to Rust really entering the public conscience/probably before it existed. AFAICT Rust takes a lot of inspiration from how you would “fix” C++, in ways that you could never actually do in C++ because it would break backwards compatibility.
[+] foerbert|5 years ago|reply
This is one of the few times I've seen anybody mention threading in Ada in this sort of discussion. I've become quite fond of the language, and my first brush over the tasking stuff left me fairly impressed/interested. However actually getting into it is something I've been meaning to do, so I don't really have much experience with it in practice.

So I'm quite curious, what problems with it you were referring to?

[+] blub|5 years ago|reply
Rust is not a good example of a language which doesn't require discipline: on the contrary, it requires very much discipline and thinking deeply about lifetimes and resources. The difference to C++ (since that's what you've mentioned) is that the compiler double-checks everything at the end. The advance in programming language usability will not come from arcane tools like Rust, but from tools like Java, Python or Go which make previously complicated things simpler with e.g. garbage collection or coroutines.

Using "scale" is also misleading, because to use again your example C++ is a mainstream programming language and one of the most popular in the world. It towers above Rust by any thinkable metric related to scale - popularity, projects, developers, libraries, etc.

I do agree that discipline isn't enough... in the sense that one can't rely on discipline to achieve safety. This is a well known principle of safety engineering, which applies to all systems, not just SW or SW/HW systems. Discipline remains nevertheless essential, because it's one of the main things influencing the "human factor".

[+] jandrewrogers|5 years ago|reply
I’m not sure “what locks it” is a useful question. Locking is a performance and scalability destroying operation in a time when we care about both. Systems that care about both largely avoid locking (including most “lock-free” locks) altogether outside of rare cases, and in such rare cases the logic is simple. Nothing is lost by avoiding locks with good architecture.

In big multi-core systems, I model the handful of locks by the worst-case access frequency for a core to understand contention. In practice, this is often on the order of 100ms, which means the locks are effectively contention-free and the fast path almost always wins.

[+] Enhex|5 years ago|reply
im pretty sure Rust builds on top of C++11's move semantics and unique_ptr.
[+] Chris_Newton|5 years ago|reply
The compiler needs that information for checking, and the programmer, and the maintenance programmers who follow behind, need that information so they know how to call the thing. On the other hand, you can usually infer the result types of expressions by a simple forward process.

Possibly unpopular opinion: I am wary of relying on type inference for return types, other than for a function defined locally where you can easily see the implementation at the same time as the code calling it. Code like

    auto x = calculate_some_complicated_thing()
    ...
    do_something_with(x)
has a similar obscurity problem to code like

    do_something_impressive(true, false, false, true)
in that to understand what the code is doing at this level, you have to read the interface documentation for the function you’re calling anyway, and then you’re hoping that you get everything in the right place because your tools might not be able to warn you if you don’t.
[+] jayd16|5 years ago|reply
I thought Java's locking worked fine as long as you didn't make your locks public or lock on `this`. What is Rust doing that's an improvement?
[+] arthev|5 years ago|reply
"The encouraging thing today is that we're kind of converging on good practices in language design." - doubtful that we'll arrive at good practices in language design through incremental steps from a flawed base. All the mentioned languages are low-level.
[+] overgard|5 years ago|reply
I really think lock-free concurrency models are the long term best bet. Locks and shared memory scale pretty badly, on top of being super hard to get right.
[+] olau|5 years ago|reply
IMHO, types for function parameters depends on the use case.

If you have a function in a standard library, types are obviously helpful, albeit it can be convoluted if they are templated.

On the other end of the scale, if you have a little helper function used to reduce duplication/increase readability in another function, explicit typing easily becomes busywork. This is where duck typing/templates shines.

I once sent a suggestion to the C++ committee people that they allowed easy templating of function parameters (something like "auto foo(auto bar)"), but I don't think people who are not used to duck typing appreciate how much it helps in the grunt work inside modules. At least, I didn't myself.

[+] andrewjl|5 years ago|reply
There's something I've always been curious about wrt `auto`, and that's why have the keyword at all? In Swift for instance, one can just use `var` or `let` to express mutability and have the compiler infer the type. Is there a particular reason C++ can't, or perhaps chooses not to, do this?
[+] ChrisMarshallNY|5 years ago|reply
Personally, I think Discipline is one of the things that separate “coders” from “engineers.”

This sounds like it’s really a treatise on “learning to build a house by making your own nails.”

Of course that won’t scale. Especially if most houses, these days, are prefab, and don’t use too many nails.

But learning how to stay out of flood plains, and selecting good prefab sources, is vital to being a builder. The first one is a fundamental discipline, that also applied to “make your own nails” building, while the second is unique to the new way we do things.

I like to write code that has a future. I want people to be able to take it over, so I use header documentation, and a consistent coding style.

I originally learned Machine Code (I started as an EE). I’ve written embedded operating systems and drivers.

A lot of what I learned, doesn’t apply to writing high-level application code in Swift (what I do nowadays), but it did give me the discipline to power through the “boring” stuff that is vital to writing good, maintainable, extensible code.

I think maybe there is a confusion between “discipline” (the type that manifests as patience, consistency, and craftsmanship), and “experienced inflexibility,” which is not always applicable (and sometimes, downright destructive, like old-fashioned C++ programmers refusing to use smart pointers).

[+] bluetomcat|5 years ago|reply
Our contemporary corporate and social culture doesn't encourage or reward discipline. We produce mostly throwaway stuff that has to be released as soon as possible on the market, or else the company goes out of business. The incentives for craftsmanship and diligence are just not there.
[+] rigaspapas|5 years ago|reply
Exactly. With so many people out there advertising themselves as "software engineers" but being "coders", companies fail to produce maintainable code without a bunch of strict rules. Coding manifestos are a must these days, otherwise you end up with a codebase that looks like the Tower of Babel.
[+] underwater|5 years ago|reply
I don't think your worldview is inconsistent with the authors.

Software is one of the few areas where we can easily and rapidly change our tools and the medium we work with. To be professional in this field you must use that ability to systematically remove issues where possible.

To fall back on the mindset that being a good engineer means eschewing those safeguards and relying on discipline is more about image that outcome.

[+] nemo44x|5 years ago|reply
I hate these articles that feature a programmer who works within a certain domain and assume all programming tasks are similar.

Yes, it would appear there is an anti-pattern of programmers that feel like going lower level for certain things matters. It doesn’t always. Programming computers has multiple levels of abstraction for solving different problems.

But there’s also this idea that the magical compiler does good enough abd there’s no reason to challenge the assembler. But there are programming problems where that is in fact essential.

It’s not a kissing contest. Writing great asm or C is as challenging as writing great css. But to be sure, knowing what your code actually requires hardware to do is really valuable. It matters and we don’t have abstraction in many cases to abstract that away totally.

It doesn’t hurt to have a good idea on how the machine works.

[+] spyckie2|5 years ago|reply
The point of the article isn't to belittle someone for having a good, deep knowledge of machines. The foundation of most code written is built by those people.

The point of the article is to point out that languages, frameworks, or apis that expect a level of discipline to write well are inferior to ones that don't expect discipline out of its coders.

The takeaway shouldn't be - I as a programmer shouldn't learn how the machine works.

The takeaway should be - if I'm designing an API/Language/framework, I should figure out how much I'm relying on an engineer's discipline to make the code great (performant, robust, secure, etc). If I can reduce that reliance, the API/Language/Framework becomes better.

[+] tonyarkles|5 years ago|reply
> Writing great asm or C is as challenging as writing great css

I would argue that writing great asm that is more efficient than your idiomatic C code is going to be a very very challenging task on modern non-embedded CPUs. The compiler definitely knows more than I do about how the internal architecture works and which instructions to reorder to prevent stalls etc

[+] nightowl_games|5 years ago|reply
As a Mid Range Millenial, I fall right into age range who cut their teeth in an era just past this guy: the jquery web dev. It's amazing to read this because I think I was part of the group that began the exponential curve of new technologies. I wasn't exposed to this "hazing" at all. I was believed in. I was valued. I was encouraged to explore.

The religion of the time was to work on things that increased developer productivity time. Performance be damned. It flowed naturally at the time. Computers were (and are) fast enough to create these web apps! We just need to get more features (and better looking ones!) out the door!

And now here I am sitting on the other side, looking at all my peers work and thinking "slow down, everyone. We've made too much stuff. We need to organize and catalogue and refine and optimize"

It's like culture is a Tick/Tock cycle. This guy was a Tock - a refinement of existing technology. I was a Tick - an explosion of new technology.

And now (I believe) we are witnessing another Tock - a refinement, a culling of the weak, a centralization.

I love reading the historical programmers account. As I near 30 I realize, that I will become that, a history lesson for the fresh minds of the era.

I think I have lots to teach you. I need only to refine, cull, centralize, and catalogue my thoughts. Piece them into theories of abstraction, class hierarchies of ideas, if you will.

For I experienced the polar opposite of this. I experienced the explosion.

And yet still, I agree.

Discipline doesn't scale. This encoding of discipline into system. I agree with it.

That defines a large part of my job - to take the "best practise" and encode it into the day-to-day.

This concept: "discipline doesn't scale" is fundamental to our history. It seems trivially true.

This prose is excellent, this history lesson is important and valued, and the fable of this story rings true.

[+] pm90|5 years ago|reply
Short of external factors (sharp recession in tech), I don't see the culling phase happening.

If a recession did happen and orgs started cost cutting, I imagine that R&D would drop somewhat, and you would see fewer new projects and toolkits perhaps? Even then its hard to see the pace of innovation (for lack of a better word) slowing.

It seems to me that technology is now in the stage, where its everywhere. Every industry that is quick to adopt new software gains a competitive advantage over its peers, in so many industries. This unreasonable effectiveness of software seems to feed itself.

I would love to see studies that measure/quantify the blooming and culling of technologies so we could talk about these things with more than just anecdata.

[+] scns|5 years ago|reply
Have you explored svelte or solidjs yet?
[+] smitty1e|5 years ago|reply
Discipline for its own sake is certainly wasteful.

What grieves me is lack of inquisitive ness and communitcation. If we're not deepening our understanding of how things work, and working together, then things get wobbly.

True story: delimited text data ingest file. Guy wrote a C language file to hack the upper byte off the characters, and then write them back to disk. At a glance we think "C! This was a performance thing." Except laboriously writing each character after hacking it in twain and discarding the upper byte isn't a performance hack. It's a "didn't RTFM on the python open() command and try the encoding argument, so we did a C hack" move. Encoding="utf-16" worked great.

I'd take a victory lap, except for every time I'm clever, there are two where I look an ass.

Discipline may not scale, but teamwork and inquisitiveness paper over a multitude of shortcomings.

[+] scottlamb|5 years ago|reply
I think it's also important to remember that these things we sometimes consider character traits can be quite situational:

In the absence of deadlines or other external pressure, I sometimes write beautiful, uselessly incomplete code. With mild deadline pressure, I do all right. In a toxic, death march environment, I can see how teamwork and inquisitiveness might drain away and someone would write the terrible code you just described.

(See also: fundamental attribution error. I wrote the bad code because of the death march. He wrote the bad code because he's intellectually lazy.)

[+] dusted|5 years ago|reply
While hardware engineers come up with faster and faster computers, software engineers come up with slower and slower and slower software.

While I agree that having nice and easier thing is nice, and easier, performance is always relevant, if not for pride or principle, at least think about the environment, computer processors and memory chips consume electricity, and are really inefficient.. How many percentage of the consumed energy turns into the desired product (computation)? Les than 1%, a CPU using 100 watt is pretty much a 100 watt room heater with a side-effect of also performing some computation..

That's a _WAY worse fuel efficient than a Model T, and yet we treat computational power and memory capacity as infinitely cheap.

[+] Slikey|5 years ago|reply
The key is that hardware has marketpreassure to become faster and faster. Software gets faster if there is market preassure (see gaming & financial). When the feature backlog is more important than performance that is obviously what is prioritized. You obviously can't work on everything.
[+] justmyname|5 years ago|reply
That's a quite unobvious consequence of the software progress. Making software be "a bunch of bricks for creating applications" do come with costs. And the main cost is performance.
[+] scatters|5 years ago|reply
Where do you get 1% from? We're several orders of magnitude further than that from the Landauer limit, if that's what you're referring to.

However, there is considerable economic incentive to keep Koomey's law going - mostly from mobile these days, since increased computations per joule means longer battery life.

[+] timwaagh|5 years ago|reply
if that's a business priority, that's a business priority. But it's not up to developers to set those.
[+] pm90|5 years ago|reply
Do you think the market is not pricing it correctly?

In the dawn of computing, machines were far less common, much more expensive, and society was willing to pay programmers boatloads of money to write optimized code because that was cheaper.

The hardware engineers did an excellent job, they reduced costs of computing by a lot. Society is not willing to pay programmers boatloads of money to write optimized code; because hardware is cheap, they can do fine without that optimization.

Whether we thing the pricing is correct or not is a different question I guess. We could theoretically raise computing prices artificially (say through taxation).

[+] User23|5 years ago|reply
Whenever I hear the term discipline in the context of computing I always think of Dijkstra's excellent book A Discipline of Programming[1]. His definition of discipline makes a lot more sense to me than the one in this article. And discipline in the sense of proficiency with the predicate calculus absolutely does scale.

[1] https://www.goodreads.com/book/show/2276288.A_Discipline_of_...

[+] fjfaase|5 years ago|reply
One of the most important reasons why we write programs, is because computers are slow and have limited memory. Computers only appear fast due to a host of tricks. The most important trick is the memory pyramid: with at the top a small number of very fast accessible memory (think CPU registers) and at the bottom a very fast but many orders of slower accessible memory (think the whole of the internet). And a lot of business is about moving data up and down the pyramid.

It looks like most software engineers are still unaware of this. Why do we still encode Unicode strings as UTF-8 strings in memory, why not as double linked of Code points? Not because we cannot, but because it is faster and uses less memory.

Another big problem is related to multi-user modifications of shared data structures and that high-level semantic modelling is hard for most people. If multiple agents (people) are modifying a data structure, then those changes need to be combined in a semantic correct way. A complication factor is the fact that most data structures are distributed (also due to the memory pyramid) and that a lot of effort is needed to synchronize modifications.

[+] heavenlyblue|5 years ago|reply
What makes a double-linked list better?
[+] juped|5 years ago|reply
>but I do occasionally still hear people telling less-experienced developers that they should learn C to learn more about how their computer works. Your computer is not a fast PDP-11, all you will learn is how the C virtual machine works.

Yes, this is wrong in that sense, but by learning C you learn how C works, and therefore how your computing systems, which are going to be C at some level because we live in Unix's world, work.

[+] quickthrower2|5 years ago|reply
I think there is a meme (in the true sense of the word, not a silly picture) where the basis of languages like C and assembler is implanted in say JavaScript and how it does things. For example references vs. values and how this affects closures. Why have a lexical scopes? All this stuff which confuses the fuck out of new programmers, might be simpler if they learned C first.

I think this can trip newbies up. Especially from bootcamps, who don't understand why their code is working, and I feel like replying with "well think about how the object is stored in memory" because none of the stuff that's built on that will make sense without it. I'd like to understand more myself. It would be useful to have some idea of what the compiler is doing with things like lexical scopes. I think that's the only way to really understand them as the abstractions are too leaky.

[+] andi999|5 years ago|reply
Although I think learning C opens up your accessible domains of programming widely. I mean, if you know C it doesnt even take a weekend to learn (the basics of) CUDA. Or you can just buy an simple embedded system and do data aquisition easily. Also there is a reason C is often under the hood of other programming languages like Python, Tcl, and (I read on the internet) Julia, Ruby and Perl.

I dont mean you can then write these languages, but you can look under the hood and read the source code (also if you want to write extensions it is useful).

[+] scandox|5 years ago|reply
> The performant is often not talked about in the same sentences as its usual companion species, the irrelevant.

What I find is that programmers I've worked with who have been the most preoccupied with performance always seems to have focused their optimizations in the least valuable areas. It's as if performance mania is directly correlated with a kind of architecture blindness.

[+] wccrawford|5 years ago|reply
I think that's because senior developers already do most of the easy/useful performance stuff the first time around. They design things so that they'll (probably) be performant the first time around because they've had problems in the past in that area.

Mid-level developers haven't had a lot of those problems yet, but they have read articles about them, and those articles usually do silly things to try to prove a point in a simplistic situation.

We had a developer that was obsessed with getting rid of jQuery for performance reasons, but repeatedly failed to recognize that the raw JS functions he wanted to use didn't work on IE and/or Safari, IIRC. Beyond that, we also supported Android 4.4's webview, and he didn't even have a way to test that. He ended up leaving the company for other reasons, but it was a constant battle to keep him in check with that stuff.

(It required compiling the Cordova app, and so long as you kept doing things the same way as the rest of the codebase, it almost always just worked. So we didn't require junior/mid-level devs to test it on every change. It did get tested eventually before the app got a new version, though.)

[+] lioeters|5 years ago|reply
This week I had to deal with the exact thing you mention. A pull request for "performance optimization", basically a proposal to unwrap a higher abstraction to do it at a lower level, manually and directly.

Yes, it does save a few function calls and instantiating a class - but that's not where the real bottleneck is. The proposed changes would require rewriting large amounts of code elsewhere, making the logic more verbose and complex.

It took some effort to convince the person that the trade-off isn't worth it. I tried to be diplomatic, but I'm afraid they took it personally that the pull request was declined.

I wish I could have proved my objection with some metrics - how many milliseconds were shaved off by the proposal, versus the increased "cyclomatic complexity", as a measurement of its detrimental effect on the code base.

[+] austincheney|5 years ago|reply
This is perhaps one of the most serious problems with software as an industry. More precise is that human investment doesn't scale, which includes: discipline, education, training, mentoring, and so forth. That is true for all industries.

Other industries know human investment doesn't scale, but even still it is absolutely professional and so it must still be accounted for. Software intentionally ignores this and then looks for things to blame when the tech debt becomes overwhelming.

[+] Hokusai|5 years ago|reply
> But along with the term of experience goes the breadth. You see, the person who learned reference counting in 1995 and thinks that you can only really understand programming if you manually type out your own reference-changing events, presumably didn’t go on to use garbage collection in Java in 1996.

I think that the article misses two points. In one side the more accessible writing code is, the better. To reduce the amount of knowledge and discipline that you need to code is good.

On the other side, discipline is still needed to design APIs, or just to maintain consistency between calculations and data as it moves thru your system, or keep technical debt in check.

> does that mean our tools should not let us write code for which there’s no test?

That depends on how your team/company wants to work. If you are doing Test Driven Development, it seems good that the tool forces you to do the test first. But, TDD is not the only or best way of developing software, so generic tools needs to support different paradigms.

I love code formarters for all this reasons. They relieve developers of the need to follow some basic code style guidelines, you use less self-discipline on writing good style and more on writing good APIs. But, to have a code formatter you need an agreed standard.

I try to make to write code easier for myself and other people, because implementing business requirements is already hard by itself. The more time you have to spend "coding" and the more you can spend thinking, the better.

[+] danbolt|5 years ago|reply
Given the time and emotional effort I've given to hear out some of my colleagues' strong opinions on computer programming in the past, reading this felt a little cathartic.
[+] dfabulich|5 years ago|reply
There are two kinds of discipline; one scales, and the other doesn't.

Consider this line from the article:

> you don’t need TypeScript to write good React Native code, just Javascript and some discipline

The author provides this as an example of the "disciplinarian" approach, which the author says "doesn't scale."

It's an interesting example, because learning a new way to code requires discipline. IME, the main obstacle to TypeScript is that you have to have the discipline to learn TypeScript and endure the compile errors it generates.

I think you could rewrite the author's example the opposite way:

"You don't need to write careful JavaScript, you just need enough discipline to learn and use TypeScript"

But this is a different kind of discipline; it's the discipline to seek out a "better" (or at least more scalable) way of doing things. You typically hear static-typing fanatics calling dynamically typed languages "undisciplined."

TypeScript is an order of magnitude less popular than pure JavaScript, even considering just new code on Github. TypeScript may be better, but it isn't taking over the world--not yet, at least--because it requires (enforces!) more discipline to get started.

[+] mdwhatcott|5 years ago|reply
The lack of discipline doesn't scale even more than discipline.
[+] einpoklum|5 years ago|reply
Well, discipline is indeed difficult to scale; however:

* There is no automation for writing good code and for good design - and that too requires discipline.

* In many cases, harnesses which relax discipline requirements have costs: dependencies, larger surface area for compromising security, performance...

* Very often, the mass of undisciplined coders rely on libraries and other core pieces of code written with a lot more discipline.

[+] ozim|5 years ago|reply
Yes of course, that is why 'special forces' in military are special, most of people are just cannon fodder.

Military orgs around the world are turning into smaller head counts of disciplined people. That is known since the Battle of Thermopylae but maybe getting lost from time to time. There is also a lot of military tech that is trying to prevent soldiers from shooting themselves in the foot, which fails from time to time.

We also know that just adding more developers to project is not increasing velocity and that small focused teams outperform large teams by orders of magnitude.

Problem we have to tackle are big orgs that need a lot of code and have high dev turnover. That is why frameworks like angular and tools first are so important, they help to scale code better than plain code or plain js.

[+] jariel|5 years ago|reply
If it's always the 'same kind of discipline' then of course what is needed is a higher level of abstraction so the programmer is freed from having to keep track of said 'disciplined parts' and focus more on other problems.

C++ while obviously useful, while it obviously 'works', and while it's existence in the context of history is also obvious ... is a giant clusterF of an anti-pattern programming language. The 'smaller, better language trying to get out' should have happened long ago, it didn't so maybe that will be Rust, but that's a big big leap and it's going to take many years yet.

[+] jb1991|5 years ago|reply
> Back in my day, everybody knew “no Markdown around town” and “don’t code in an IDE after Labour Day”

what the heck do those phrases mean?

[+] jlnthws|5 years ago|reply
Discipline is all what matters, automatizations are imperfect and temporary tools.

Simply put: all abstractions are leaky, all abstractions have trade-off, and you cannot necessarily rely on some abstractions in every context.

The GC example that the author uses is a good one: having GC in a language does not magically remove all memory allocation issues. Believing this means you just don't have much experience programming (or you've been very lucky). I've been working mostly with GC languages and we have memory leak every now and then, good luck to fix that without discipline. Embedded software frequently relies on (very) limited compilers and libraries, most of the time there are no GC and until recently there were even no malloc/free for $B things shipped to space: only static arrays and discipline my friend!

TCP is another good example: sometimes you need to craft you own congestion management, packet discard rule or what not.

ORMs have made my life easier but I've still had to spend days on complex hundreds lines SQL reports. The list goes on.

It's not "I had to go through this, you should too" out of frustration. It's because at some point the same kind of issue will appear again, even though the current available abstractions may seem to shield us from it.

It might not scale as easy as you wish, yet you need some disciplined seniors to review and teach your juniors. This very article ironically proves it :)