Hacker News new | ask | show | jobs
by PaulDavisThe1st 1771 days ago
Things I've learned from 25+ years of programming in C++ (and 5+ years of C before that):

1. not all software is about pushing and pulling to/from a database; if yours isn't, be sure you understand why that's the case.

2. "backends" (not "web backends", but the more general "where the mechanisms are") should know nothing about "frontends" (again, not web, but the more general "user interface of some kind"). This is really just MVC in its most basic sense. One good way I've found to think about this is to assume that there's always at least two UIs running simultaneously. Make sure this can work.

3. if your program has a user interface, everything the user can do without further interaction should be represented by a closure that can be invoked from anywhere (but always in the correct thread).

4. single-threaded GUI code seems like a limitation but in most projects, it's the right choice. By all means use helper threads when needed, but never allow them to use any API that's part of your GUI toolkit. Knowing that your GUI code is ALWAYS serialized is a huge conceptual assist when reasoning about behavior.

5. access to an excellent cross-thread message queueing system is likely to be a must if your software uses threads. This should include a way for one thread to cause arbitrary code execution in another thread.

6. direct memory access for the UI is nice from a programming perspective (that is: just directly call methods of backend objects), but can erode the wall of separation between the UIs and the backend.

7. lack of direct memory access for the UI(s) can significantly impede performance, but enforces a conceptual clarity that can be valuable.

8. when notifying the View(s) about changes in the Model(s), there's a tradeoff between fine-grained notifications ("frob.bar.baz.foo just changed") and high-level notifications ("something about frob just changed"). Finding the sweet spot between these two can be a challenge across the life of a long-lived piece of software.

9. lifetime management will never be trivial. Accept it, and move on to thinking about how it is going to work even if it is not trivial.

10. try to refer to as many things as possible indirectly. if something has a color, don't make it's state refer to the color, but the name or ID of the color. do not over-use this pattern when performance matters, but also do not over-estimate your ability to understand when performance matters.

4 comments

> "backends" (not "web backends", but the more general "where the mechanisms are") should know nothing about "frontends" (again, not web, but the more general "user interface of some kind"). This is really just MVC in its most basic sense. One good way I've found to think about this is to assume that there's always at least two UIs running simultaneously. Make sure this can work.

This is one I constantly struggle convincing my colleagues about. It becomes much more "obvious" if you are trying to write unit tests in C++ code[1], but unit tests are a mere side benefit. It's more about reducing coupling.

Currently working on a code base that outputs to an Excel file. We recently started dealing with more data than the Excel file can handle easily, and the system came to a crawl. So we had to allow for the option to output to CSV (easily 100x faster in our use cases). At least now some of my colleagues have a bit of appreciation on what I've been harping on.

The Excel library is still intrinsically tied to much of our code. We've been getting over 15GB RAM usage for data that I'm sure would not take more than 2GB if we manage to bypass the Excel library.

[1] Why does my class that computes X need to know that something called email exists? So to write a test for this class I need to instantiate a whole other set of classes just for output? Just have it "ReportMessage" on the Reporter interface and let whatever class that inherits from it figure decide if the message will go out via email or SMS.

40+ years of programming experience here. The next step is to realise that MVC is just an example of events driven programming. An application is a state that “instantly” changes to the next state when an event happens. Multiple separate states (logging/UI/DB/remote/…) coordinate by reacting to events. The business logic is generating “this is now true” events. That’s it. There is nothing else to it.
That might be true for something where the phrase "business logic" is applicable.

But in my niche (realtime audio software), there is underlying data in the system that changes over time independently of events. So there is "something else to it".

I use the term “Business Logic” when I talk about the code that decides what is now true when an event has happened. I real-time audio software it would be the logic that decides which sample to play next and what the (say) audio volume should be now. The non-logic code is the code that read events from the environment (user input) and changes the environment (the audio hardware).
Not meant to be pedantic, just playing devil's advocate, but isn't the real time audio bitstream just a continuous source of events that gets blended with the rest of the application state like active filters and what not?
Individual samples do not in any significant sense constitute events. The only thing that really pays them any attention is metering, and the result of that process is only displayed to the user periodically (i.e. something roughly equivalent to the screen refresh rate).

Even higher level objects, such as what are various called "clips" or "regions" or "events" frequently pay no role in any type of event notification system. In some designs, the boundaries of such objects may play a somewhat event-like fole.

A lot of wisdom to unpack here. Thank you for sharing!
number three comes from Javascript specifically?
Not at all. I've likely written no more than 100 lines of JS in my entire life. This is an observation based on C++ development, exclusively, but I believe likely relevant to any language that can implement something semantically equivalent to a closure.