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.