My Experience with Web Development from a Systems Programming Perspective
In this blog post, I’ll describe my personal experience learning about the modern web development ecosystem (programming languages, frameworks, programming paradigms, tooling, etc.) and compare it to my experience doing systems programming in languages like C, C++, Rust, and Go. If you’re a systems programming developer and you’re interested in switching to modern web development, or if you’re a web developer with curiosity about systems programming, this article may be of interest.
From Systems Programming to Web Development
It’s common in many dynamic organizations that engineers are free to explore technologies and programming languages. Taking the time to familiarize yourself with different technologies can help you make better decisions about current or future products, as you often have a better understanding both of the different ways you can solve a problem and the tradeoffs involved. At PSPDFKit, experimenting is even part of our core values, a set of values that we agreed on collectively as a company.
As part of this experimentation, I switched technologies for a particular project and went from doing systems programming in C++ to some web development in TypeScript. In the following sections, I’ll describe what stood out about the experience, specifically in terms of programming languages, programming paradigms, frameworks, and tooling.
Programming Languages: TypeScript vs. C, C++, Go, Rust
(a) => a + 100;
For someone not familiar with the language, the syntax above can easily be confused with an assignment. It’s also not easy to come up with words to describe the syntax to search for the underlying concept in the documentation. It’s true that C++ has complex grammar, and some parts of it are pretty daunting (ie. template programming), but if you restrict its usage to only a small part of the language, the syntax is more or less bearable. C and Go are extreme examples of having simplicity in the grammar of a programming language, so you’ll probably need some time to adapt to the complexity of TypeScript’s grammar.
Programming Paradigms and Frameworks (React.js, Redux…)
TypeScript is a multiparadigm language, which means it supports multiple programming paradigms (procedural, object-oriented, functional). Compared to a language like C++, the functional paradigm in typical TypeScript codebases is more pervasive. C++ also supports the functional paradigm, but it isn’t as popular. Possible reasons for that difference may be that the C++ syntax is less powerful and more verbose than the TypeScript one, or that C++ developers may be afraid that functional programming and functional data structures slow the program down. There are ongoing efforts to improve C++ functional programming capabilities in future C++ standards, but it’ll take some time before developers can use them in codebases. Modern systems programming languages like Go, and especially Rust, are designed to better support the functional paradigm, so if you come from one of those programming languages, the transition to TypeScript will be easier, as you’ll probably have more experience with designing your program with functional programming in mind.
Regarding the common frameworks for application development, systems programming and web development mainly differ in the number of them, and especially in the rate at which new ones are created. While systems programming typically depends on a few well-known libraries that are considered stable (STL, Boost, and recently, Abseil), the web development community isn’t afraid to use a high number of base libraries or frameworks (for example, React.js, Angular.js, and Redux.js.). New ones are introduced quickly, and codebases are keen to adopt them promptly, so you need to be prepared to constantly learn new frameworks. While this trend is common in software development in general, in web development, the cadence of learning new libraries is more frequent.
Build Systems and Tooling
To produce software from source code, specialized tools are necessary: a compiler/interpreter, a build system, and possibly some dependency management system in the form of a package manager.
tsc, along with a build system with package manager support. The most popular package managers are yarn and npm. These are a welcome addition to someone used to C or C++, which are languages without a standard package manager. If you come from Rust, you’ll also feel at home with yarn or npm because the way their package managers work is similar.
Simplifying the addition of new dependencies can have its drawbacks, like suffering from an explosion of small dependencies. Dependencies are essential for code reuse, but it’s not uncommon in the web ecosystem that a project incorporates lots of dependencies for small pieces of functionality. One extreme example of a very small library is the is-odd library, which is a library whose only purpose is to return if a number is odd or not. Depending on lots of small libraries can cause problems in the long term, like having to deal with incompatible versions, unexpected security problems, etc. Sometimes it’s easier to implement the functionality yourself instead of depending on a small library. I recommend reading an article by Russ Cox, Our Software Dependency Problem, which talks about the importance of handling dependencies effectively.
Compile times in systems programming languages are usually slow, which contributes to a slow feedback loop overall. Web development offers a faster feedback loop, and thanks to this, you can iterate on the product you’re working on much more quickly.
IDE tooling is an important feature for the makers of programming languages for the web, so its quality is usually good. The latest language standards are readily supported, and the refactoring capabilities provided by off-the-shelf IDEs seem to be good enough. C++ is a particularly complex language to parse and index, so the refactoring capabilities have always been somewhat limited or unreliable.
On the negative or somewhat negative side, web development tooling changes very quickly, and it’s common for projects and libraries to require “bleeding-edge” tooling dependencies. This isn’t usually bad, but it can cause unexpected troubles. Systems programming environments are more conservative in general, and it’s normal for a project to keep using an old version of a compiler and dependencies without any external force, like a third-party dependency, forcing the upgrade.
Switching from systems programming to web development isn’t like using another programming language to solve a different set of problems. Both the nature of the problem that web development tries to solve (UI/UX in a web browser) and the legacy/tradition of the web development community influence the way development is done.