Today, we are open-sourcing the Swift Talk backend, written in Swift.
The Ruby Beginnings
Before launching Swift Talk, our weekly video series, we spent much of our time trying to figure out the best format for an episode. We experimented with many pilot recordings, discarding every one of them before settling on our current conversational style. To help us focus on the content we decided to outsource our backend development, working with an external contractor to build the site in Rails. This was great for launching, but very quickly our limited Rails experience, and the major differences between Swift and Ruby, made changes to the backend very time-consuming.
When refactoring in Swift, we rely heavily on the strong, static type system. For example, this allows us to change a method signature, then rely on the compiler to guide us through all call sites and any tests that are in place. In Ruby or Rails, you can’t rely on a static type system when making changes. Instead, you rely exclusively on an extensive test suite, and your knowledge of the code base and the frameworks to guide your refactoring.
Ruby on Rails also relies heavily on meta-programming, which makes it possible to achieve a lot with very little code. Likewise, if you’re not intimately familiar with the framework it can be quite hard to trace through your application and understand what is happening.
Finally, even for such a simple project, our Ruby code base had a lot of dependencies — 75 to be exact (some were transitive, but most were direct dependencies). Over time these needed to be updated, and we had to make sure that all the dependencies and our code kept working together as expected.
Rewriting the Backend in Swift
After finishing our latest book, App Architecture, we decided to spend our newly quiet days tackling the backend rewrite. A rewrite would have no direct business value, but even so, the project would be fun and interesting, and this can be reason enough. We wanted to prove to ourselves that Swift was feasible as a backend language, that we could rely on the type system for refactoring, and that we could make do with minimal dependencies.
Over the last few months, all of these have turned out to be true.
Preparing the code for public release, we wanted to clean it up and make sure that it was readable. We extracted non-app-specific parts into frameworks, conducted some large-scale renaming, and refactored many things. The compiler and type system were a huge help.
We now only have six direct dependencies: the web server library SwiftNIO, a Postgres client, a Markdown library, an XML parsing library, an encryption library and SourceKitten for source code highlighting. Most notably, we’re not using a web framework, but are building directly on top of SwiftNIO, so that we can structure the code in our own way.
Good News: it Works!
So far, we love working on this code base. With so few dependencies we own almost all of our code, and can design and extend it in ways that works for us. We are no longer stuck with a Rails backend that we found hard to change, and we can add features quickly, such as the new team member signup pages.
For us, the biggest downside is that Swift on Linux isn’t battle-tested. We ran into a few bugs (most notably around URLSession/URLRequest behavior), but they were easily solved or worked around. Battle-testing has to start somewhere, and we expect this will only improve with time.
We aren’t the first to build a project like this in Swift. Point-Free was written in Swift at launch, and it continues to be an inspiration — it’s no coincidence that our Dockerfile is so similar to theirs.
We’re planning to add more features to Swift Talk, and we hope that open-sourcing our code base will inspire other people to write their backend in Swift as well.
The full source code is on GitHub.
If you’d like to learn more, our latest public episode starts a new series exploring what it’s like to work with the backend. The rewrite also informs many of the episodes in our Server-Side Swift collection.
To follow the series, you can subscribe.