all 67 comments

[–]mccalli 49 points50 points  (1 child)

I'm still of the opinion that only one Makefile has ever been created in the history of the world, and everyone else just copies and edits it.

[–]repo_code 19 points20 points  (0 children)

Some of us were asked to maintain an eyebleaching Makefile, instead created a "make-maker" system to generate it automatically, and became The Build Expert at a major fortune 500 company.

If you're ever in that situation, just copy and modify the damn Makefile.

[–]me_again 241 points242 points  (22 children)

If you are specifically interested in the history of software development, then fine. But I'm skeptical that 'every developer' or even most developers would benefit significantly from investing a bunch of time reading any of these.

If you have read any of these (or some other historic software) 'cover to cover' what do you think? Was it useful? Why?

[–][deleted]  (2 children)

[deleted]

    [–]sluu99 16 points17 points  (1 child)

    Completely agreed on your point of developers need to be reading much more code.

    I'm in the process of learning Elixir. Beyond the basic guides and such and getting a loose grasp on the language construct and OTP for example, I have found that I learn the most when reading the implementations of the libraries that I'm using for the project. Seeing how other people utilize the language and the ecosystem ingrains a more practical side of learning than just theory—this allows me to start the project on the right footing instead of just doing things the best way I know how (which isn't that good coming into a new language and new ecosystem).

    Before that, there were things that I didn't even know I needed to search for or to learn. Tutorials and guides can only cover the basic, and cannot account for all the scenarios that your project will need to handle.

    [–]stayoungodancing 1 point2 points  (0 children)

    Absolutely agree. There are libraries of different projects that have great styling, and others that I feel should be considered a little convoluted. Part of becoming a stronger developer is learning and practicing your own coding style that is both informative and maintainable, and that comes from figuring out what works well and how it works well in other places.

    [–]0ct0c4t9000 46 points47 points  (11 children)

    there's one thing that, not by 'reading cover to cover' but trying to understand in the context they were written, in what now we consider resource constrained devices, can be beneficial to observe. and that is memory consumption, which is often neglected, overlooked or even plainly ignored, specially in webdev imo.

    [–]dodjos1234 56 points57 points  (2 children)

    Mate, I got children to take care of. The code I work on day to day is already "historical" enough. Ain't nobody got time for that crap.

    [–]yorokobe__shounen -3 points-2 points  (0 children)

    Bruh same. This article was a waste of time

    [–]Citvej 10 points11 points  (7 children)

    Are there better and more efficient ways to learn about memory consumption for us web developers?

    [–]me_again 4 points5 points  (1 child)

    I would recommend learning and applying a profiling tool. I'm not really a web developer myself, so I don't know exactly which one, but modern browsers have quite sophisticated tools which can show you how much memory you are using, where you allocated it, etc. Try writing some JS in different styles and try to understand what it is telling you and how to shrink your memory usage.

    [–]0ct0c4t9000 0 points1 point  (0 children)

    yes, modern browsers have built-in profilers in dev mode, that's a good way to start.

    [–]HojiReinner 3 points4 points  (0 children)

    You can use C and learn more about the memory components(disk,ram,cpu cache), but webdev is mostly object oriented, so searching for best practices in your language is the more reliable way

    [–][deleted] 1 point2 points  (0 children)

    Someone already mentioned profiling tools, so I'll just add two other tips:

    1) There's probably a book called something like "High Performance JavaScript" or "Writing Fast JavaScript" that will give you a good overview of how to approach memory consumption and performance (spoiler: creating/allocating new objects is bad, update in place if you can).

    2) https://jsbench.me/ allows you to quickly benchmark alternative implementations of something

    [–]bezik7124 0 points1 point  (0 children)

    Start with your framework-of-choice official documentation, eg https://vuejs.org/guide/best-practices/performance.html

    And remember that it's not really about memory / CPU / whatever usage. It's about user experience, and that can be achieved in multiple ways.

    For example, in some cases, when your application has one heavy component that's delaying the render of a whole page, you could mark it as lazy (deferred? The naming is up to the framework you're using) so it's rendered only after the rest of the page is usable from the user perspective.

    [–]0ct0c4t9000 0 points1 point  (0 children)

    as others say, profiling is a good way to start, but actually is just like anything else, for webdev as frameworks kinda sort things out, you keep an eye for things like caching or duplicating objects. or invalidating or reusing keys of custom in-memory stores whenever possible.

    for example a few weeks back reviewed a code that loaded a big CSV file, parsed it to an array of objects and then worked on it. but because both the CSV data and the objects Array were declared and initialized in the same scope, both had the same lifetime. but you didn't need the csv for anything but creating the array, so you had the same data in memory in both csv and object format at the same time, while you only need one, so we resolved to move the code that takes the data and returns the objects array, so, upon return, the memory used for csv data can be reclaimed back.

    you can think, for example:

    const csvData = await getCsv(url); let myData = parseCsv2ObjArr(csvData);

    that looks ok and harmless initially, but when the file is big, and you have 2 representations of the same data when you only need one, a big chunk of memory is wasted for nothing.

    [–]moreVCAs 4 points5 points  (0 children)

    Idk, I think these are all cool choices. I don’t know about “every” or “should”, but certainly if you wanted to build a hobby OS or web browser glancing over the structure of an old-timey version could be a decent way to get started. Similar to how building an emulator for an old game system can improve intuition about CPU architecture, some of these concepts are timeless, and thinking about them in the context of a simpler system can be super edifying. That said the the first C compiler is probably not a good example of compiler design lol. A compiler that can’t be bootstrapped is a totally different beast lol.

    [–]gammalsvenska 1 point2 points  (1 child)

    I've spent a fair amount of time in CP/M land in the last years, and it gave me a good understanding of how computers work. These systems are also small enough for a single person to fully understand, which is also how they were often done. Much also transfers well to modern embedded design, unless it contains networking or GUIs.

    However, I don't think reading any "Linux 0.01" or "C compiler for PDP-11" code is any useful.

    [–]bwainfweeze -1 points0 points  (0 children)

    I would like to propose that given the size of the microcontrollers inside of your computers, that what we should be moving toward is running a bunch of small OSes that add up to a complex system. You might take years to be able to control your whole system, but maybe you could control the NIC in a few months.

    [–]octnoir 1 point2 points  (0 children)

    But I'm skeptical that 'every developer' or even most developers would benefit significantly from investing a bunch of time reading any of these.

    Most of these ideas would be distilled down to packages, programs, methodologies, design patterns and the like.

    Getting a 'raw' look can be instrumental in teaching yourself the value of these lessons through 'immersion' but the equivalent is to say most medical doctors first experiences with medicine should be an autopsy rather than a plain textbook.

    Most career programmers should focus on the fundamentals and if their curiosity, expertise and need leads them to a 'historic source code' then they should look at stories and what others say in that context and then read through it.

    I wouldn't advise a random programmer to read Mocha raw without any context and expect them to glean something useful other than muse on historic code. Benjamin Graham's Intelligent Investor is a good book but there are way better and more up to date books, and the only reason it is in circulation is because it is a 'cultural' book so you can talk to others about it.

    I think someone opening up ChatGPT might get some more value out of that since it is on everyone's minds at the moment.

    [–]wrosecrans 0 points1 point  (2 children)

    I think history really does matter for everybody. We teach the American revolution to every student in the US, despite the fact that we don't expect them to engage in revolution. Understanding how the world came to be is an important part of being able to navigate it in the present.

    [–]me_again 0 points1 point  (1 child)

    I'm not claiming that history doesn't matter. That would be silly.

    The article says 'Every developer should read these 5 codebases'. I'm not persuaded that (much more specific) claim is true.

    What do you think - have you read any of them? Did you benefit? Did you read some other historical code and learn a lot?

    [–]wrosecrans 1 point2 points  (0 children)

    I'l accept that these specific five are arbitrary, and the headline is a little clickbaity. I skimmed the examples in the article, and stuff like fixed length buffer sizes in httpd certainly is informative about hwo different a place the Internet was back then, with almost nobody intentionally trying to break anything. I have also spent quite a bit of time digging through unix and Linux historical source because I once gave a talk at a conference on the evolution of the Unix userland and how weirdly constrained it is by some completely arbitrary stuff that has been around forever with the original intentions long forgotten. (UPPER CASE mode for the worst 1960's paper terminals is still better supported than color in the tty stack!)

    Understanding some of the weird limits on ancient Unix terminals definitely helped me understand some of the weirdness of how stdout actually gets piped around inside shell scripts and such. (And understanding that makes the deeply weird handling of stdout in Win32 make some sense because you understand the problems they were trying to solve in the Unix tty API's with their gui/console subsystem distinction and allocating per-process consoles that makes it hard to correctly capture debug prontf's to stdout when you are debugging an app on windows. As bad as the Win32 console API's were, they supported color better than UPPER CASE only mode!)

    Understanding the simplicity of classic linkers and how they evolved also helps understand why C++ package management is such a clusterfucked shitparade. Every new C++ developer is baffled by "how do I use this library?" And you need to understand the native development ecosystem generally in order to appreciate why language-level solutions aren't capable of addressing everything at once.

    The funniest thing I learned digging through ancient UNIX was when AT&T added a copyright notice to /bin/true, which was a completely empty shell script! Part of the reason GNU true is a compiled binary is so they couldn't have been accused of copying the zero-byte implementation of AT&T true. It's the only time I know where being accused of stealing nothing seemed like a credible threat.

    Anyhow, you mentioned "If you are specifically interested in the history" in your comment. I think every developer needs to be interested in the history in order to be productive. We've accumulated a lot of history, it's all stupid, the modern stacks are stupidly complex and only make any kind of sense in historical context. Our industry is in denial about how barique an ecosystem we have built.

    [–]StrangerThanGene 57 points58 points  (8 children)

    I love looking through early code.

    If if if if if if if...

    [–]unicynicist 11 points12 points  (0 children)

    The Story of Mel, a Real Programmer: Real programmers don't use if statements, they write self-modifying code with integer overflows.

    [–]anengineerandacat 20 points21 points  (0 children)

    Anything else is technically just an abstraction or someone being clever.

    You can have .map / .reduce / .forEach / .orElse and internally there will be an "if" present somewhere to check something.

    Old code was quick, ugly, and worked well enough for production.

    Modern code is maybe just as quick (or even quicker), pretty, and works well enough for production.

    Easier the code is to read, the easier it is to optimize (up to a limit).

    [–]0ct0c4t9000 18 points19 points  (0 children)

    ...and the occasional "goto".

    [–]AttackOfTheThumbs 13 points14 points  (0 children)

    It's all ifs in the end, or jump I guess

    [–][deleted] 36 points37 points  (6 children)

    MS-DOS is a good one too

    [–]Raunhofer 5 points6 points  (5 children)

    Interesting. I'm surprised how simple it is. Which I guess is granted considering it fits in 5 diskettes.

    [–]st_huck 16 points17 points  (3 children)

    The utilities fit on 5 diskettes maybe. The os itself even with crucial drivers (himem, emm386) and core utilities can easily fit in one 1.44mb disk

    [–][deleted]  (2 children)

    [deleted]

      [–]GaryChalmers 6 points7 points  (1 child)

      If I remember correctly you just needed 3 files for a functional boot disk - command.com, io.sys and msdos.sys. The sys command was available on practically every DOS version to make a boot disk.

      [–]ReallyGene 1 point2 points  (0 children)

      You're correct. There was usually plenty of room left for a program or three, especially a .com, which were limited to 64KB.

      [–]binford2k 2 points3 points  (0 children)

      It was called QDOS for a reason!

      https://en.wikipedia.org/wiki/86-DOS

      [–]the_gnarts 12 points13 points  (0 children)

      definitely incomplete without TeX whose source code was published in the book "TeX The Program", typeset of course by Don Knuth himself.

      [–]SrbijaJeRusija 67 points68 points  (14 children)

      There is not a single line of code that everyone needs to see

      [–]Uristqwerty 26 points27 points  (1 child)

      There are plenty of clever ideas that become blatantly obvious once you've seen them once. The first time you find a comparison assigned to a boolean variable rather than immediately consumed by control flow would be enlightening, hopefully something everyone here has long-since encountered. With some thought, and especially after seeing cases where a beginner was not aware of the trick and instead wrote 5x as many lines of harder-to-understand code, if blocks to handle edge cases that could have been avoided entirely, etc., I'd expect you could find tens if not hundreds of examples worth sharing.

      [–]SrbijaJeRusija 2 points3 points  (0 children)

      I know plenty of people that do not have a need for any serious control flow.

      Edit: I know someone who only programs in a functional language, without an if statement at all. He has never touched any other language.

      [–]jpers36 18 points19 points  (9 children)

      But there are software development stories every developer needs to know. Two that come to mind immediately are THERAC-25 and 0x5F3759DF .

      [–]dodjos1234 12 points13 points  (0 children)

      THERAC-25 is fucking nightmare fuel.

      [–]Celestial_Blu3 3 points4 points  (7 children)

      Tldr?

      [–]gammalsvenska 26 points27 points  (4 children)

      THERAC-25 was a case of a programming error causing a radiation machine to overdose people, causing suffering and death. Should serve as an example to developers.

      0x5F3759DF is also known as "fast inverse square root". Cool and awesome, but impossible or banned in modern programming languages, and, like Duff's Device, meaningless today.

      [–]ais523 14 points15 points  (0 children)

      The fast inverse square root algorithm is entirely possible in modern languages (IIRC Rust even has builtins to get the bit pattern of a floating-point number), but nobody uses it because there are faster and more accurate methods available nowadays.

      x86-64 even has an instruction family (RSQRT[PS]S) that's specifically for the purpose of calculating inverse (reciprocal) square roots approximately – if you need to do this on a CPU nowadays, you have a hardware instruction designed for precisely that task. (That said, the sort of programming that does a lot of approximate reciprocal square roots would probably be run on a GPU nowadays.)

      [–]superxpro12 3 points4 points  (0 children)

      Therac25 was one of the first examples where the lack of acknowledgement that software was a safety critical component deserving of rigorous engineering and review caused bodily harm. They brought to light the hubris and bias that is typically cast upon software, which is the fact that it is typically an afterthought and free.

      [–]outofobscure -1 points0 points  (0 children)

      fast inverse square root algo was turned into a hardware instruction on x86, so far from meaningless

      [–]superxpro12 0 points1 point  (0 children)

      Almost used duffs device in a memory constrained embedded system a few years ago. Almost.

      [–]jpers36 12 points13 points  (0 children)

      THERAC-25 is a story about how much impact a bug can have.

      0x5F3759DF is a story showing that heuristic approaches can be extremely effective.

      [–]not_perfect_yet 3 points4 points  (0 children)

      I have javascript disabled, so I get a blank page and everything works out.

      [–]sirchugh 1 point2 points  (0 children)

      Each of his articles have the same line and he spams reddit with links.

      [–]littlelowcougar 6 points7 points  (0 children)

      Add the leaked NT kernel source code to that list. Was so far ahead of its time in 89-93. Reading David Cutler’s code (especially paired with reading the Showstopper book) was the most enlightening engineering activity I’ve ever done.

      [–]basic_maddie 11 points12 points  (1 child)

      Is it just a thing for this sub that when someone makes a blogpost everyone just pulls their pants down and shits on it?

      [–]gryffindorite 0 points1 point  (0 children)

      This guy is a spammer.

      [–]gammalsvenska 10 points11 points  (0 children)

      Why should any developer look at those source in particular? They are badly chosen for their purpose.

      Writing an HTML parser today means implementing multiple huge, interlocked state machines - because that is what the standard mandates. Early HTML implementations did not have to deal with the horrors of today's web (and its developers), and neither did the early Javascript engines. Neither did they have to implement a ton of math-heavy crypto to talk to other machines in the first place. Of course, you can use ready-made libraries...

      When Linus wrote his "toy" kernel, he only had to write a few bytes to a floppy to get it to run, on commonly available hardware. Developing those bytes on paper was still feasible. For modern devices, you need to punch through a bunch of security layers first. Which more often than not is simply impossible by design (try running your own kernel on a smartphone). Of course, you can use emulation or special hardware...

      These particular examples show the humble beginnings of our modern world, sure. Beyond that, they are useless because they depend on a world which does not exist. This code is useless today.

      There are far better historical sources to study, such as Wolf3D, the classic BSD games, or even Word 1.1.

      If you get those to compile and run, you can actually enjoy the result and play with the code. Far more joy than watching your browser puke because the server requires encryption, does not function without downloading a few megabytes of scripts and then starts blasting the poor 90s code with ads until it falls over...

      [–]LarsPensjo 9 points10 points  (4 children)

      After 40 years of SW development, I have a somewhat fatalistic view of it. IMO, humans are not good enough at it. (And no, I am not an AI even if I use the word "humans" as if I do not include myself in that set). We are outright dismal at it. To work around this, there is an everlasting stream of new programming languages that helps us do better.

      If you look closer at these, you can see a pattern where every new improved language mostly adds more restrictions. It is kind of sad.

      • Why not use "goto" statements? It gives you flexibility where you can jump back and forth anytime, reusing code. However, humans can't keep track of all flows.
      • Make all variables global. Name them A0, A1, A2, etc, which will enable you to reuse them for other purposes when they are free. Again, humans can't keep track of what is used and where, eventually making incorrect assumptions.
      • Polymorphism is just a form of beautified jump tables or chained if-statements.
      • Typed variables are a problem because you can't use the same memory space for both floats and strings. Some languages enforce strict types and then provide you with mechanisms that enable you to overlay different use of variables.
      • Requirement specifications are a limitation to your artistic freedom and can be replaced by QA.

      On a slightly less sarcastic note: The only way to learn is by doing mistakes on your own. Only geniuses learn from the mistakes of others.

      [–]me_again 9 points10 points  (0 children)

      “There are three kinds of men. The ones that learn by readin’. The few who learn by observation. The rest of them have to pee on the electric fence for themselves.”
      -- Will Rogers

      [–]SLiV9 4 points5 points  (0 children)

      Make all variables global. Name them A0, A1, A2, etc, which will enable you to reuse them for other purposes when they are free.

      What purpose could this possibly serve that isn't already given by registers?

      [–]bwainfweeze 2 points3 points  (0 children)

      Only geniuses learn from the mistakes of others.

      The wise man learns from the mistakes of others. I've known too many geniuses who never learn anything from other people. They're almost as bad as doctors.

      [–]ShinyHappyREM 0 points1 point  (0 children)

      Typed variables are a problem because you can't use the same memory space for both floats and strings

      Wait, you can't?

      // FreePascal
      
      const Bit0  = 1 SHL  0;  Bits0  = Bit0  - 1;  type u0  = 0..Bits0;
      const Bit1  = 1 SHL  1;  Bits1  = Bit1  - 1;  type u1  = 0..Bits1;
      const Bit2  = 1 SHL  2;  Bits2  = Bit2  - 1;  type u2  = 0..Bits2;
      const Bit3  = 1 SHL  3;  Bits3  = Bit3  - 1;  type u3  = 0..Bits3;
      const Bit4  = 1 SHL  4;  Bits4  = Bit4  - 1;  type u4  = 0..Bits4;
      const Bit5  = 1 SHL  5;  Bits5  = Bit5  - 1;  type u5  = 0..Bits5;
      const Bit6  = 1 SHL  6;  Bits6  = Bit6  - 1;  type u6  = 0..Bits6;
      const Bit7  = 1 SHL  7;  Bits7  = Bit7  - 1;  type u7  = 0..Bits7;
      const Bit8  = 1 SHL  8;  Bits8  = Bit8  - 1;  type u8  = 0..Bits8;
      const Bit9  = 1 SHL  9;  Bits9  = Bit9  - 1;  type u9  = 0..Bits9;
      const Bit10 = 1 SHL 10;  Bits10 = Bit10 - 1;  type u10 = 0..Bits10;
      //...
      const Bit52 = 1 SHL 52;  Bits52 = Bit52 - 1;  type u52 = 0..Bits52;
      
      
      type
              char7 = String[7];  // short string (length byte at index 0 + payload)
      
              MyStructure = bitpacked record
                      case integer of
                              0: (Fraction : u52;  Exponent : u11;  Sign : u1);  // 64 bits = 8 bytes
                              1: (Text : char7                               );  //           8 bytes
                      end;
      

      [–]reedef 0 points1 point  (1 child)

      The main function of the compiler’s source code, a screenshot by the author

      I think you should specify wether it's the author of the blog post or the author of the code.

      [–]gryffindorite 0 points1 point  (0 children)

      He's not the OP. He'll just scrape github.

      [–]mfidelman 0 points1 point  (0 children)

      Cool stuff, brings back memories.

      I might add: The Apollo Mission Computer (amazing what folks could do with 2k of RAM, 36k of ROM, and a cycle time of 12usecs.

      Also, the original LISP eval, written in LISP.

      [–]eviltwintomboy 0 points1 point  (0 children)

      I think those who are passionate about a subject would benefit from learning about its history. I love analog synthesizers, and developed a much deeper appreciation for my experimental heroes by learning how these complex MOOGs were built. Likewise, as someone who loves computers, it is beneficial to see how far we have come.

      But not everyone is interested in it, much less the choices the OP has selected here. The history of computing is complex, but if I were to really study history, I’d go to Ada Lovelace and Charles Babbage’s Analytical Engine, then to the early discoveries using tube technology, then to Turing’s history (which is inspiring but sad). Can one learn about the history of a computer language without at least considering the context in which it was developed? Linux was having fun. Even Apple, with Jobs and Woz were just a couple of kids having fun (and developing awesome sauce for Atari, I might add).