What can be learned from studying long gone development practices?
shape-of-code.coding-guidelines.com
shape-of-code.coding-guidelines.com
An awful lot of languages (I'd say all commonly used ones) use if/then/else, do..while, for..next, and so on, but I can't remember the last time I saw a program with a complicated control flow done by gotos.
Such things were common in the 1970s, in fact the first professional program I ever read did it that way, and it took me ages to work out what was going on.
One particularly confusing technique was to goto a computed expression. Leads to all sorts of interesting bugs.
The whole point of structured programming was "Don't do that! You can do everything you want to do with a small set of restricted control structures, and the control flow is much easier to read".
I'd say that idea won so hard that we don't even notice it.
And I agree by the 70's spaghetti code with goto's was on the way out - though some early GWBASIC code had some gnarly practices.
In 1960, it was the only way to do "if" in the most popular programming language that existed at the time. Today, I bet that most programmers haven't even heard of it.
I've run across this in C demo code for a chip designed within the last 10 years. It was all goto soup in a god function.
You might see weird stuff in embedded code because it needs to interact with flakey hardware using a flakey compiler, and be cycle accurate in the process.
The way these systems get developed is a huge part of the problem. One team building hardware, another building the software no one communicating. Everyone hoping for the best. Frantic cleanup in software to make it all work on top of hardware thet can no longer change. I am sympathetic to the embedded developers position, but they still seem to exist in a different universe from modern software dev.
As a security practitioner I don’t blame the devs. I like to ask questions about how things got to be this way. How we can make it better. But make no mistake. A decade will pass and some embedded developer will still be linking some ancient binary or throwing together a bunch of sketchy CGI files that let an attacker scribble everywhere in memory. Embedded is like, the final frontier of software security :)
That’s my lens anyway :)
;)
Unless your C implementation has TCE, don’t assume recursion is optimised.
I find recursion to be very simple and elegant for prototyping algos, but I would instinctively rewrite it with an iterative/queue form as soon as the "toy" stage is over.
Or you could, you know, use a normal loop...
Besides, since 2005 or thereabouts gcc with -O2 did proper tail-call optimisation so that the stack doesn't grow on a tail-call recursion.
These two are not as intertwined as you make them. OCaml has mutation but not iteration.
A better example might be Clojure, which has mutable variables (opt-in, like OCaml), but no traditional imperative loops - only recursion. However, even then, the syntax for its tail-recursive construct uses "loop" as a keyword.
You're saying !immutable -/> loops (not p does not imply q)
It's a fair thing to mention, but you're not gainsaying anything I said; I never said anything about mutable languages that don't have loops. In fact, to the topic at hand, you would assume that to be the case, given that structured programming was intended to force loops on languages at the time (mostly mutable) that weren't using them (but instead were using gotos and the like)
Perhaps not "complicated" enough, but here's a fizzbuzz in spitbol (a fast SNOBOL interpreter). This is an unstructured, goto based language for non-numeric computation developed at Bell Labs. Would love to see a faster version in some popular, "structured" scripting language in the same number of characters. I find I can write scripts in more aesthetically pleasing ways than with "structured" languages. But of course aesthetics is subjective. (The term "subjective" here means what looks good to you might not look good to me, and vice versa.)
;var a = 0
;start a = a + 1
;break lt(a,101) :f(end)
;x01 y = remdr(a,3)
;x02 z = remdr(a,5)
;x03 x = y + z
;x01a eq(x,0) :s(x06)
;x02a eq(y,0) :s(x04)
;x03a eq(z,0) :s(x05)f(x07)
;x04 output = 'fizz' :(start)
;x05 output = 'buzz' :(start)
;x06 output = 'fizzbuzz' :(start)
;x07 output = a :(start)
;end
Perhaps the unintended benefit of gotos is that it becomes foolish to try to construct overly complex control flow. Whereas stuctured programmming seems to encourage control flow complexity. The use of the phrase "complicated control flow" in the parent comment is a great example. It suggests for the author structured programming allows and arguably therefore encourages such complexity. But what benefit is served by creating "complicated control flow". Why not aim for simpler control flow.What's the difference between :(label) / :s(label) / :f(label) / :s(label1)f(label2) ?
s and f are different conditions
s=success
f=failure
default is unconditional
Is bitwise OR a built-in or would it require writing a separate function.It looks like computing x as remdr(3) + remdr(5) is a pure mistake. You only care about the case where remdr(15) is 0, and it's true that remdr(3) + remdr(5) is 0 if and only if remdr(15) is 0. But you're calling remdr twice where you should only be calling it once.
(Or, really, you can keep the weird calculation of remdr(3) + remdr(5), which is conceptually incorrect but correct in every case that matters, but stop computing it with two extra calls to remdr. You're already calculating those values and storing them in the variables y and z; compute x as y + z, which must be much faster than calling remdr two additional times.)
As a side note, why do you want to use "a language for non-numeric computation" for this purely numeric problem?
If the purpose of fizbuzz is to test for programming language selection, and fizzbuzz is a "purely numeric problem", then should the proper choice be FORTRAN.
Looks like it’s unconditional, conditional (success), and conditional (failure) jumps.
:s(A)f(B) is how you tell it to jump to A on success and B on failure.
:($('A' B))Bullshit. There's so much that objectively deficient about that piece of code that I'm having trouble taking you seriously. If that comes at all close to being a representative sample of someone trying to make clear, readable code in that language, then the language was clearly deliberately designed to have an excess of visual noise. But I suspect you're not actually trying to make that code look as clean as the language allows.
The semicolons serve no apparent purpose. Having labels like x01 and x01a with no functional relationship between those lines is outright hostile to the reader. Using x as the prefix for almost all of your label names and as one of your variables is mildly confusing, especially when most of those lines don't relate to the variable x and don't appear to need labels at all. "output" seems to be a magic variable for which assignment has side effects, but nothing in the syntax differentiates it from any other variable. Grouping all the conditionals together, then grouping all their consequences together forces the reader to jump back and forth between lines more than if you put the consequence of each conditional immediately after the corresponding test (in this instance, your way does have the advantage of making the conditional fall-through pattern more readily apparent, but on the other hand that wouldn't be as necessary if you could use more than one line of code to write a single line of output).
You can claim all you want that aesthetics is subjective, but at the very least you must recognize that visual noise you've trained yourself to ignore is objectively different from code which doesn't have those distractions in the first place.
semicolons separate statements. Dont like them at the beginning, move them to the end.
Dont like the label names. Rename them to whatever suits you.
The thing that you may be misunderstanding is that this is written for me, not you. It is written to please my sense of aesthetics, not yours. I am not employed to write programs for other users or to be read by programmers. Every user is different.
You probably could not write a program I would use. I would find "deficiencies" and reject it for something else.
If programming is objective then why are there thousands of different languages, and the number is constantly increasing. All of them are basically doing the same things.
Honest question. Why are there so many different languages. Whats behind that.
Well, unless you are the only programmer that company will ever has, you should write code that others can read and understand. Or if you are working in academia, then you can write as you like, but as a professional, you should write readable code for those coming after you.
GP might work for a company working on a niche application (be it software or hardware) that requires code to be written like the aforementioned and anyone who specialises in that field is already adept in reading and writing code like that.
I'm not going to defend that choice of programming style for everywhere but software development is a broad industry where you do get pockets of people working on code that looks totally alien to others who are used to see C or ALGOL derived languages. Take LISP for example, there is LISP code in half of all European TVs shipped in the 90s. Forth is used in loads of places, even FreeBSD's bootloader is written in Forth. Machine code is almost completely unreadable for humans yet a massive chunk of software in the 80s was written using it.
My point is the industry might have standardised on a subset of syntax styles that seem readable to the lowest common denominator of developers but it's a massive industry with developers using all sorts of exotic languages effectively. Let's not go down the rabbit hole of saying they're doing it wrong because their code looks alien to you before you've understood the field in which that code is running in.
Sometimes I wonder if the only people who browse this site are JavaScript monkeys and Rust fanboys. There’s a whole spectrum of languages out there that looks nothing like C.
There was a story that appeared on HN many years ago and I wish I could find it again. It described a gentleman in Japan who did not work in the computer industry but did some sort of (let's call it) "optimisation work" on his own as a hobby. The story told that the work was being used by companies within the computer industry, I think it was hardware manufacturers. If I recall correctly he did not have any formalised education in computer science or mathematics. I am probably getting some details wrong; need to find this story again.
1. Personal pride.
2. Personal or corporate need for control
3. Personal or corporate need for visibility/fame.
4. Licensing unfit for desired use.
5. Technical or social impossibility to extend existing language to fit target space.
Some people like different tools for reasons other than pride, ambition, need for control, etc.
Some things are more enjoyable for some people. Chocolate vs vanilla kind of thing.
1. Might happen occasionally but rarely would anyone adopt a vanity language
2. Nope
3. Lol this is just a rehash of 1.
4. Languages can’t be licensed, compilers can. And that’s why we often have multiple different compilers.
5. Now you’re getting close.
The actual reason is:
Trying to solve a different problem
Believe it or not, problems can vary massively from one field of IT to another. Some require low level hardware access, some need mathematical compatibility, others might be optimised for text parsing, and sometimes you just care about developer throughput. There’s macro scripting, web development, games development, server side software, OS development, and firmware. And entire industries using their own specialist tools that most people don’t even know about unless you happen to already work in it.
Some languages are designed to be bullet proof and used in aviation or other domains where failure isn’t an option, some are the electronic equivalent of duct tape designed to allow people to quickly stick operations together.
Programming is such a massive industry that you need different languages for different problems.
C# - Created by Microsoft after Sun sued them over Java. But C# (or rather .NET) did still solve several problems with Java’s JVM (eg it’s much easier for end users to work with, it supports native widgets, first class Windows support) and personally I find C#’s syntax a little more grown up too. It’s also worth noting that C# has since evolved into its own language (rather than just being a Java knock off).
J# - there isn’t any other functional language that targets .NET so there is a distinct problem J# seeks to solve.
TypeScript - That language clearly aims to solve several short comings of JavaScript, such as type safety.
Kotin - I don’t know much about this language personally but the Wikipedia article answers my point for me:
> In July 2011, JetBrains unveiled Project Kotlin, a new language for the JVM, which had been under development for a year.[17] JetBrains lead Dmitry Jemerov said that most languages did not have the features they were looking for
Go - this filled a massive hole at the time: languages that had the guarantees of a statically compiled language while being as quick to learn and develop on as a JIT compiled language and which were cross platform. I actually adopted Go in version 1.0 for a personal project specifically because it solved problems with the other mainstream languages - problems that were very relevant to the project I was creating.
I’m an old fart with 30 years of software development experience across well over a dozen different languages (you could easily treble that if you want to include machine or compiler specific dialects too). The problem many have when they see the term “general purpose” is they assume it means “best suited for all purposes” but actually it just means “can solve most problems with some degree of success”. Thus there will naturally be specific domains where even the most versatile languages are superseded by another language. And that’s fine. Good programmers can adapt to using the right tools. Whereas bad programmers will reach for the same tools regardless of the problem because they’re too lazy or too ignorant to learn anything else.
I don't agree the GPs code is more aesthetically pleasing but he's right that it is subjective. All the counterarguments you've given are subjective to you (eg you know what tokens do in over languages, spent zero time learning the tokens of this language and thus you're clearly not going to know what a colon does in this language just by looking at it).
One of the hardest things it seems for a developer to do is accept that their personal opinions are subjective since they spend so much of their career being told there's a correct way of doing things. Your post here is a classic example of that. Arguments about strongly typed and loosely typed languages are another. Debates about functional, stack backed, object orientated, procedural, etc etc... it's all just people like yourself trying to convince others that your subjective view is empirical scientific fact. But it's not, it's just opinions. Some opinions just happen to be more popular.
I tried very hard to only bring up problems that were at least somewhat objective. Counting the number of superfluous symbols in the code is not purely subjective. Noting the similarity between "x01" and "x01a" is not purely subjective. Measuring the distance between a conditional test and its consequences is not purely subjective.
And I did figure out what a colon does in this language just by inspecting this code. It's semicolons that serve no apparent purpose in this snippet, and even with the outside knowledge that they serve the same statement separator purpose as in C and many other languages, it's still an objective fact that they are largely redundant. The character count could have been a lot lower if the language made it the default for new lines to start new statements and used a special character for continuing a statement on another line.
Subjective would have been if I complained about the semicolons being at the beginning of the line instead of at the end. That's almost purely a matter of taste driven by prior experience with other languages.
Subjectivity isn't an absolute all-or-nothing question. An issue having some degree of subjectivity doesn't automatically put it into a purely subjective, "all answers are equally valid" realm.
> eg your attempt at providing fact while your evidence is just more illustrations of familiarity which is entirely subjective).
I referred to some quantifiable metrics. Those have nothing to do with familiarity. Whether a number is larger or smaller than another number is a fact unaffected by your level of familiarity with either number.
You're really not responding at all to the substance of my comments, so you really don't have any justification for calling me "laughable" at this point. There are plenty of ways you could try to substantively refute my comments, instead of rejecting everything generally while mixing in insults.
An easy way to see that these issues aren't subjective in general is to ask why absolutely no-one uses the kind of code given above unless they're forced to because of the platform they're using, e.g. a constrained embedded environment or whatever.
You either have to conclude that effectively all developers have the same subjective take on these issues, or that there's something beyond subjectivity at work. There are very good arguments for the latter.
The goto's you've used here are within the set of allowable uses, so can be mapped directly onto structured constructs. There's literally no advantage either way, other than introducing the possibility to the reader that there could be something weird going on in your code.
> Would love to see a faster version in some popular, "structured" scripting language in the same number of characters.
How's this?
#include <stdio.h>
void main(){
for(int a=1; a<100; a++){
int y = a % 3;
int z = a % 5;
int x = y + z;
if(!x) puts("fizz");
else if(!y) puts("buzz");
else if(!z) puts("fizzbuzz");
else printf("%d\n",a);
}
}
Same technique, give or take some unimportant details. Not particularly golfed. 240 characters to your 312. I have no idea if it's faster because I don't know how good the spitbol interpreter is, but I do know that because I've used "structured programming" structures, the compiler (or interpreter, whichever you prefer) can in principle make more safe optimisations to my code than to yours.If C doesn't meet the "scripting language" constraint (which I find arbitrary, but not insurmountable), I'd throw Julia or FORTH at it and probably shave a considerable number of bytes off.
Unoptimised, it's already roughly 3x faster than the spitbol script.
Moving the "int" specifier out of the for loop allows this to compile under -std=C89. How does one suppress the warning about the non-int return value for main().
There's an enormous amount of accidental/inessential complexity in the code you provided, and that's a big part of the reason no-one seriously uses SNOBOL or similarly primitive languages for anything serious today. Code like the above can't remotely scale to larger programs, and attempts to do so were notorious for their failure.
Here's a version of FizzBuzz in e.g. Haskell:
fizz n | n `mod` 15 == 0 = "FizzBuzz"
| n `mod` 3 == 0 = "Fizz"
| n `mod` 5 == 0 = "Buzz"
| otherwise = show n
mapM_ putStrLn $ map fizz [1..100]
Even the already rather simple control flow is abstracted away by the 'map' function, so there are no visible loops or gotos. Compare that to your program with a dozen labels and seven gotos, where the actual logic is buried in irrelevant cruft.Even versions with more explicit control flow (i.e. without map) can be similarly readable, but if your goal is to minimize complex control flow, then rather than reverting to intractable gotos, go read "Lambda: The Ultimate GOTO" (http://dspace.mit.edu/handle/1721.1/5753), published in 1977, and learn to use functions for control flow.
fizzbuzz:{v:1+!x;i:(&0=)'v!/:3 5 15;r:@[v;i 0;{"Fizz"}];r:@[r;i 1;{"Buzz"}];@[r;i 2;{"FizzBuzz"}]};`0:$fizzbuzz 100My favourite part of the Knuth paper is this:
begin until error or normal end:
if m = max then error ('symbol table full') fi;
normal end;
end;
then
error (string E) =>
print ('unrecoverable error,'; E);
normal end =>
print ('computation complete');
fi;
This is pattern-matching on a Result<(), String>, in 1974. How adorable!It’s a shame that innovation in this area has stagnated so much: most languages only feature IF, WHILE, FOR, single-level CONTINUE/BREAK, SWITCH (with fallthrough), and GOTO as an escape hatch in case the others are insufficient, just because they are so familiar. Only now languages like Python or Java are getting any form of pattern matching. Though props to the latter for at least allowing to break multiple loops by way of labelling the outermost one.
On a side one could argue that early returns, while often used, are not structured programming and a goto in disguise.
Writing corresponding nested maps/folds is generally quite unsightly. Plus the resulting code may be non-tail-recursive which means that the sentinel EarlyExit value will get checked and re-returned for every function in the call stack on such exit.
This loop construct, however, basically introduces locally scoped continuations that you can directly return to. Of course, it's possible to optimize nested maps/folds down to this form, but it requires very heavy inlining — Haskell does it because it's semantics allow deforestation, but does Rust do it?
If it were not for being autorejected in code reviews I would use goto:s alot more.
Even if I want to break out of nested for:s there is always whining about jumps.
In some cases it makes the code more readable as Knuth shows. Dogmatism is a bane of programming.
You can easily emulate GOTO much the same way.
Huh. I suppose the modern-day equivalent is commonly done with higher-order functions - in particular, callbacks, CPS and returning functions. I've worked with people who find this confusing (particularly the last one - functions returning functions).
In languages like Java that want to hide everything memory related I also was struggling with grasping what a function really is. Is it even allowed to return a function?
Languages that hide memory locations and direct memory access are one of the reasons that some programmers are struggling with functions returning functions.
That depends. If the language supports proper first-class functions, it's basically an instruction pointer plus a record of the variable bindings the function code is "closed over" (i.e. parameterized on). Many languages do support this nowadays.
I'm not capable at all with assembler, can't write my own compiler.
But my model of how a computer works, in my head, is "functions are just sequences of bits arranged as CPU instructions." I know how it works, even though I can't write a compiler or parser.
And it's absolutely essential to how I program, even in the highest level languages (SQL explain plans).
> Today, structured programming appears remarkably simplistic, great for writing tiny programs (it has an academic pedigree), but not for anything larger than a thousand lines.
Curious. I rather think that structured programming became so ultra-pervasive in any high level programming language younger than 50 years old or so[1], that we've lost the extra name for it. Practically every programming language that isn't either pure functional (rarer than it seems), low level (i.e. assembly), or very very domain specific (e.g. SQL[2]) is a structural programming language.
Examples for "structural programming languages" are C, C++, C#, Java, JavaScript, python, Go, Swift, Scala, PHP, perl, Rust, D, ... You get the idea. If a general purpose language does not follow it, it's something notable, like Haskell.
So rather, I think this is an example of something that was so utterly successful that it became absorbed into the general fabric of mainstream programming. It's true that we don't draw as many flowcharts as we used to, because we got more comfortable with everyday programming, but they are still how we think about code a lot, and for complex processes that we want to visualize we still do draw them.
[1] Not a scientific estimate. Substitute "not very very old" if you like.
[2] Funnily that stands for "Structured Query Language", but not necessarily for significant reason: https://en.wikipedia.org/wiki/SQL#History
That said, I'm pretty sure that TeX is more than a 1000 lines of code.
This is because most modern languages allow you to write in structural style which is impossible to get wrong without noticing. If you have a loop, you know it repeats for its condition, and does not otherwise. If you have a `then` clause, you know it will only run when the `if` condition is true. If you have a function, you know it's only computed when called.
Compare it to pre-structural languages like Fortran IV or Basic, where you can freely jump into the middle of the loop, inside of what constitutes an undelimited `then` clause, or even into the middle of a function, without "calling" it properly.
This allows for potentially very clever code that saves every last byte and cycle. It also allows for totally mind-boggling, insidious bugs in code we'd now consider plain and foolproof to write.
Can someone please tell the clang compiler folks?
Loops are sometimes more restricted, but even then, neither C nor Pascal prohibit it (Algol-60 did, curiously), and many languages derived from them that retained goto do the same. Off the top of my head, the only mainstream language I can think of that specifically prohibits goto into inner blocks is C#.
“TeX was the first faily large program I had written since 1970; so it was my first nontrivial ‘structured program,’ in the sense that I wrote it while consciously applying the methodology I had learned in the early 70s from Dijkstra, Hoare, Dahl, and others. I foudn that structural programming greatly increased my confidence in the correctness of the code, while the code still existed only on paper. Therefore I could wait until the whole program was written, before trying to debug any of it. This saved lots of time, because I din't have to prepare ‘dummy’ versions of nonexistent modules while testing modules that were already written; I could test everything in its final environment.”
That said, there are still 337 instances of goto in tex.web by my quick and dirty count (grep goto tex.web | grep -v '@' | wc -l), which is perhaps an undercount since there are modules that might be included multiple times thanks to how the WEB system builds its Pascal output and I don't have a Pascal reformatter to make analyzing tex.p manageable.¹
1. The choice to "compress" the Pascal output of tangle is one of the most inexplicable choices made in the development of the TeX ecosystem.
Even if most language have structured flow-control, a lot of people NOT do structured programming.
This is how I remember it back in the day and how I do it (curiously, much better on Rust, where it match better how was done on Pascal! ie: Not OOP, big on structs, functions, enums) and is a mix of:
- Define the major components of the app and put it on "modules" (aka their own file or folder if truly large)
- Define the major structures according to the domain (aka: make tables in sql). This is what POCOs are used today or plain Rust structs.
- Make procedures that operate on the above with a clear in/out discipline. Also, document that (this one, I always forget!)
So, is not that far away from functional, inmutable programming, only that:
- A lot was mutable and pass references. This is also a big part of how you discipline to define the flow make this tractable or not
- You don't have generics or classes, so you stick to plain data, plain vectors or lists and duplicate stuff (algos) here and there (no way to abstract "map/filter/folds" that I remember)
- You must be disciplined with the naming of "modules", "POCOs" and functions. Also limited in the length of names (like files only being 8 chars), causing the rise of Hungarian notation (that was misunderstood/misused badly!)
- All your pipeline are eager (not chance of iterators and stuff) so you repeat "loops" everywhere
- Everything was more self-contained, that is great... as long you don't forget the discipline.
"The discipline" was the key here, for make this actually "structured programming".
The MAJOR point is how you STRUCTURED the program, not the use of structured control flow!
The structured program theorem is also about specific control structures, essentially proving that they are Turing complete by themselves. I remember that we had to prove in university that as a result, you can transform any program into a program with one big while loop. (I think that's equivalent to "μ-recursion", whereas the simpler "LOOP" statement only implements "primitive recursion" which is not Turing complete, but it's been a while.)
What you talk about sounds like it could be "modular programming", of which Wikipedia states: "Modular programming per se, with a goal of modularity, developed in the late 1960s and 1970s, as a larger-scale analog of the concept of structured programming (1960s)."
Of course, Wikipedia is not a good source, I wasn't there when it started, and it's entirely possible that structured programming meant more back then than it seems to mean today, or meant different things in different circles. A quick Google search seems to confirm that nowadays structured programming primarily seems to be the use of control structures, though maybe that's just what survived?
As it stands, what they link to, and what most seem to understand as structured programming, was not a fad at all.
Also, a lot of good practiques was from books or conferences (mostly sponsored by MS).
Brooks wrote The Mythical Man Month in the 1970s about his experience in the 1960s and it is still extremely relevant today.
When I was starting my software development career in the mid 1990s and trying to understand how to manage the process, one of the things I did was write to NASA and request a copy of their Manager's Handbook on Software Development.
This was not because I wanted to run things like NASA. That would have been be horribly inappropriate for a startup dev team. However, I wanted to understand an extreme; a process where spec writing, testing and on time delivery were prioritized.
I never used anything directly from that NASA handbook, but I learned a lot.
Almost 30 years later, I still have that book. It's one of my little treasures.
[1] https://everythingcomputerscience.com/books/nasa-manage.pdf
[2] http://www.cs.umd.edu/projects/SoftEng/ESEG/papers/83.88.pdf
In the 90s it took a (very friendly) FOIA request, a $5 processing fee, and three weeks to get my hands on that handbook.
It's both a reasonable and unreasonable concept. It's unreasonable because it tries to turn programming into factory work, in fact you may even see them set up workflows predicated on an assembly line concept. This kind of works, and why it's kind of reasonable, when the work is sufficiently well-understood. It's very common in these systems to have a very detailed specification. Often the defects are deviations from the specifications that got through because of the manner in which these were classically tested (primarily integration tests, often manual, necessarily restricting the scope of the testing regimen). Other defects are realizations that the specification itself has an issue (happens), often the result of a dependence on a prose format for the specification which doesn't lend itself well to formal analysis. It also tends to, well, become wordy. 1000 page specs are not unheard of even for relatively small systems, you can imagine there may be quite a few pieces of conflicting information in there.
Nothing has changed in Brook's book because it's a book about human beings doing creative work, and humans haven't changed at all.
By the late 1960s, we had realised software development was hard. We put some good brains on the problem. Discussions between leading experts brought up several very important points, that we struggle with to this day:
- Naming things,
- Low coupling and high cohesion,
- Communication between developers,
- Communication with customers,
- Evolving prototypes,
- Avoiding the planning fallacy,
- Estimation,
- Support and understanding from upper management,
- and much, much more.
These are problems we struggle with today, but it's also the problems identified by and worked on by the experts of the late '60s into the mid '70s. We can learn a lot from what they discovered. (I sure have -- and I keep learning more!)
In fact, if I'm being a bit uncharitable, only three things truly seem to have changed since the late '60s:
- We have faster computers.
- We have virtual computers.
- We have higher level languages.
- We have version control.
Other than those four things, all advances in how to build software I've seen the last few decades are, in some sense, a rehash of what they found out early on.
The idea of mass development with large, open ended access to code is actually a surprisingly recent idea. Plus of course Stackoverflow has significantly changed the approach.
Concurrent programming is also significantly different to its 1960s iteration. COBAL and Fortran did not even attempt to do such a thing, the handling of all those asyncs was not around.
Many many other architectural details have also been implemented that were specifically designed not to be exposed to give classically trained programmers the illusion that things under the hood have not changed. However, they have significantly. Branch prediction leaps out at me, but also many other forms of evaluation, memory and processor design.
There is more than you describe that has changed.
If you would say that compilers and parsers have not changed, that would be extremely accurate. But there's a bit more to the world on either side of the compiler.
Do you really think branch prediction has significantly reduced the difficulties in developing software, or altered the way software developers work? Bringing up things that have changed behind a compatibility layer that makes us program against an emulated PDP-11 sounds to me like support of my thesis, if anything: the only changes to the way we work are faster computers and higher level languages.
> - We have faster computers.
> - We have virtual computers.
> - We have higher level languages.
> - We have version control.
> Other than those four things
I see off-by-one errors hasn't changed since the '60s.
Surprisingly, very little has changed.
Setting goals and delegating responsibility is how things are supposed to be led.
Substituting that with project management is a pathology.
And that problem is where the input can be modelled as a stream of structured events.
This may sound too abstract, but it includes, for instance, pretty much any file. But the value lies more in that it makes it trivial to design a file format that is easy to parse, and process.
For me the value lies in that in the old days techniques were developed to systematically solve programming problems that will always we relevant, in this case processing a stream of events.
It is very well explained in the follwing MIT ocw lecture (it uses Java, but is actually language agnostic):
"Designing stream processors
Stream processing programs; grammars vs. machines; JSP method of program derivation; regular grammars and expressions"
https://ocw.mit.edu/courses/electrical-engineering-and-compu...
To practice it, you could try "Project 1: Multipart data transfer" in the link below:
https://ocw.mit.edu/courses/electrical-engineering-and-compu...
Pretty much every SE methodology has been a fad when it claimed to be the One True Way, and has offered useful ways of solving a certain kind of problem.
I don't think that follows at all. Unix shell programming is very much a bottom-up philosophy where you write a bunch of generic tools and then string them together to solve your actual problem at the last minute.
I guess what I'm saying is that the complete input state of the program is known the moment you hit enter.
As opposed to a GUI where a user could flip a switch at any point during the program's lifetime and mess with its internal state, which feels like it maps better to OOP.
I don't know. Personally even for a fairly small tool I'd want to build it bottom-up by composing smaller functions, and I think you'd gain a lot of reusability that way - indeed I'd say many of the classic unix tools suffer from being programs rather than libraries, and it's a shame there's such a hard boundary between shell and the tools it uses.
If anything I suspect the causality goes in the other direction - if you want to make a big system using procedural programming and C, then you have to split it into processes that are simple enough to comprehend in their entirety, and that have a very simple state model where they essentially just take input and give output and don't have any persistent state between invocations. But when you put it like that, Unix shell-and-pipeline style starts to seem like an effort to invent functional programming, just at a somewhat more coarse-grained level.
It’s the exact opposite. Unix was if not a reaction at least and antithesis: top-down programming is BDUF, you first decide what the entire thing does, then you go down a level and define / implement what’s supposed to be there.
Unix is about starting from small utilities at the bottom and jury-rigging them until you get something you like.
Funnily enough, this is how GNU libc is organised.
The issue is that often the quality is good enough to have something that works and then the buyer/payer does not want to spend more. Problem with that is it makes the maintenance and future adaptations expensive and can turn the system into one where fixing one bug creates 1.2 new bugs.
Therefore I think it is wise to err on the side of caution and spend more on software quality than seems to be needed immediately. It is a bit like in the early days of bridge-building the bridges needed to be built stronger than needed because it was not possible to calculate the safety margins accurately.
1) Create a division of labor so that tasks could be pushed to the cheapest person available.
2) Create a waterfall with documented buy in each step of the way.
3) Train an army of smart people for 6-8 weeks and cut them loose in specialized role. (Either a process person writing requirements or a tech person coding them or a tester confirming the code does what’s needed or change person teaching users)
4) Design and conceptual integrity of the whole were frequently missing.
5) The system buckled when requirements were wrong or changed.
6) Terms like “software factory” were used. When they’re used to today they reflect this mindset.
7) The best programmers were trapped in a system that wouldn’t promote them beyond a certain point because they weren’t generating revenue or managing a large group of people.
8) The sum of this was an industry notorious for overdue and over budget projects.
8.5) People prayed for their overdue projects to get cancelled before their portion would be blamed.
Software development isn’t perfect today but we are in a much better place.
> There is one technique for producing a software system that rarely gets mentioned: keep paying for development until something good enough is delivered.
Highlights.
Even shorter: loop a = lt(a,100) a + 1 :f(end) output = (eq(remdr(a,15),0) 'fizzbuzz',(eq(remdr(a,3),0) 'fizz', (eq(remdr(a,5),0) 'buzz')),a) :(loop) end
I would say that the mythical man month concept has stayed and still is talked about today even though the book itself is also hopelessly outdated except maybe in IBM style enterprise software.
What parts did you find dated? I haven't read it in the last few years, but I still get reminded of concepts like Second System Effect every once in a while.
TLDR: Whatever software building methodology/process you are using now, is probably crap, and made up by people that don't really know any better and just making things on the go.
Eg. He mentioned 'structured programing' was a fad at the time. Now it is Agile, and its offshoots.
Best way to deliver software is to have a handful of capable people and pay them well and get out of their way. The key to get paid well as a dev. is to be in a project that is important, life/death for the company.
I think the most interesting part of the article was the early job divisions while building software. It didn't make sense, but it was adopted due to the coorporate culture of the time.
That is the gist of one of the essays in The Mythical Man Month mentioned in the parent.
Does not invalidate your actual point, though.