Vertical Integration

In the future this post will describe how Raven is a vertically integrated, self-contained package where everything you need is included, is lightning-fast, and just works out of the box, but I'm still writing it.


Plugins Are Outsourced Innovation

Many IDEs offer core features through third-party plugins. This is a big mistake.

If a feature is important, the designer of the IDE should've thought of it, and built it directly into the IDE with native support. Since it's important, it should be tightly integrated with and enjoy the same optimizations as the rest of the application.

Depending on plugins to offer important features means relying on other people to vote on what features the IDE should have. This is abdicating the responsibility of creation. Also, plugins are separated from the rest of the app, and from each other; having lots of plugins means fragmented functionality. An integrated development environment with plugins is literally not integrated.

Plugins also tend to suffer in quality. Plugin developers aren't as invested as the core developers to offer unreasonably high quality. They don't have the insight into the app's internals that one needs to build good, fast software. They don't understand the app's performance model; many an initially performant app ends up being slowed by a popular but inefficient plugin. This is partially because plugin writers are expected to reason about their plugin using the API provided by the app, which is usually (a) a collection of crappy black boxes that (b) can't be easily changed after a while, since now existing plugins are depending on their stability.

Apple eschews plugins in favor of thinking hard about what the most important features are, then building them into the app with an extreme level of quality. This is the right answer. Plugins give lesser designers a means to outsource innovation, which is synonymous with being fucked.

Here at Raven we regard any IDE that offers Vim keybindings through a plugin as a non-competitor.


Raven's Approach to Performance

Raven's biggest feature is its speed, which we accomplish by (a) refraining from doing flagrantly slow things, and (b) eliminating complicated abstractions that make it difficult to keep an eye on which parts of the code are growing slow:

  1. Limited Subset of C++. Raven's basically all C. We use C++ to overcome the inconvenient low-level nature of C — we use member functions, autolambda, and a gross defer macro — but we don’t structure our app in C++: no private fields, constructors, new, inheritance, polymorphism, RTTI, or STL. Instead of RAII we just zero our memory.

    This has the delightful consequence that when we want to know how something in our code is done, we just go and read the code that does it. There's no more jumping back and forth between ten classes, trying like a detective to mentally piece together a history of events. This lets us see immediately if we're doing anything slow.

  2. Treat Errors As Another Code Path. We don’t use exceptions for ordinary error handling, because errors aren't exceptional. They should just be another code path with well-defined behavior. Our program doesn't suddenly do something mysterious (and maybe slow, who knows) when an error happens.

  3. Custom Memory Management. Calling malloc is a great way to drop a frame. We allocate a huge chunk of memory at the beginning, then manage it ourselves using fixed-size buffers, stacks, arenas, and pools (see Game Engine Architecture). This (a) is much faster, and (b) forces us to actually think hard about our memory usage, instead of pretending we have infinite memory and being shocked when it turns out we don't.

    This model of memory is actually more consistent with reality. We only get a fixed chunk of memory that we have to manage ourselves, you say? You mean kind of like an actual computer?

  4. No External Libraries. When you’re building something hard, external libraries cost more than they're worth. Anything written by someone else is code you don't own. You end up having to (a) beat your program into the right shape to integrate with the library and (b) reason about your program in terms of the constructs the library gives you. God help you if the library has a bug, or does something stupidly slow.

    Far better to own all of your code, so your program lies entirely within your field of vision and control. We only use two libraries in our whole app, ImGui and stb.

  5. One Process, One Thread. Our whole application is one big update-render loop. No IPC, synchronizing multiple threads, or client-server architecture with JSON over HTTP. Poof goes a whole source of bottlenecks, complexity, and bugs.

    We make some exceptions; our parser uses threads to parse multiple files at a time. But we don’t use processes or threads to partition our application into modules.