It would be good to see some numbers for this, because this is definitely not my experience when it comes to building (and testing) Java/Kotlin
BSD is not currently supported, but Fedora and Debian are. You can download the toolchain for those platforms at Swift.org - Install Swift
Are these the final images you're deploying? How big do they end up? Most images I deploy are ~150MB, and you can make them even smaller using Alpine or Chiseled containers.
FWIW I've built and deployed many, many server apps using Xcode
I like your list and want to add another option of unchecked exceptions:
(5) do not return on errors immediately, record the fact of the error and continue the processing. it would be possible to check if the error occurred and skip some calculations, and finally the error will be propagated and caught by the outer catch block. With this variation "x + y" would "raise" the error flag, then "lock.unlock()" be executed normally. At some outer level the error will be caught and handled accordingly.
i donāt have issues with deployment images, but development images are indeed very large, as they are bounded by the uncompressed[1] size of the Swift toolchain which is around 1 GB in size.
this takes several hours for me to download at home (70kb per second), although it takes only a minute or two at my local public library. so whenever i need to download the Swift toolchain locally, i take a stroll down to the library to use their internet connection.
i suppose this is the type of thing that is unnoticeable to people working at large corporate offices but it can be a serious barrier to entry for others looking to get started with Swift.
[1] actually i remembered it is compressed, i just remembered the 1GB number because i am using a two-toolchain setup for cross compilation
Important point. Swift is optimized and natively built during compilation, giving you good startup times and (together with not using a tracing garbage collector) lower memory footprints during run.
So with Swift:
Use the much faster debug builds during development. Iteration time during development is good.
Deploy pre-built release builds (no compilation on deployment servers).
Yes, there is no āinstantā deployment after changes, your CD strategy might need adjustments.
Concerning sizes: No need for the Swift toolchain when deploying executables. There is ongoing work on making static executables smaller. On e.g. standard Ubuntu no additional Linux packages are needed for deployment if Foundationās XML part is not used.
The reason I mentioned this, is because the documentation is quite misleading. The toolchain exists for development on Debian and Fedora, but according to the link on that same page, Debian/Fedora are not listed as a supported deployment platform. And minor things (e.g. the availability of pre-installed timezone packages) make swift programs panic (crash) in production.
Now compare this to the very explicit and well documented platform support for Rust, or Go, for example.
Final images can indeed be shrunk, however for profiling, testing, generating docs and other purposes, we need images including the Swift toolchain and its dependencies which is ginormous. It really costs quite a lot in storage and traffic to download/store/update these.
So are Rust, Go, Python. Even Kotlin, when deploying to prod, one may choose ahead of time compilation for faster startup in "tiny" compute environments.
The features of the language should not influence the cost of using it to such an extend.
Here are some examples of paper cuts which make Xcode seem lacking for server app development (to be clear: maybe not impossible, but require more effort to setup than one is willing to put into something like this):
Hot reload :).
@Helge_Hess1 already mentioned deploy and debug on remote systems be it dev containers or remote hosts. e.g. We debug most of our server apps on dedicated VPS/VM instances so they're "developed" on the same environment they will run on (no Docker), source code is on a remote filesystem over ssh, or attach a debugger/profiler to something like this.
Xcode intellisense suggests methods from system/iOS/macOS frameworks which aren't appropriate for a server app. It's clueless about server specific libraries and imports. Merely starting a new server project in Xcode requires fiddling with the Scheme/Build Settings to force Xcode into the correct path and other minor things as it assumes we're building a (console) app.
Also pointed out by @Helge_Hess1 - Xcode has no plugin support: Various ancillary file formats and tools (like Jinja2, Smarty, real JavaScript/TypeScript support, YAML, JSON, Python, SQL, CSS, lint configs, querying a SQlite db, etc.) - all the things one does when making a server app.
Just for the record, we are doing server apps and have never touched any of those things (except json which is built into foundation).
Not all server apps had anything to do with front web severs - although many do - but letās call those āweb server appsā or something then to avoid conflation of the different kinds.
This may also impact oneās view of pain points with āserver appsā.
ā¦and neither Rust nor Kotlin count as languages that compile fast (altough Kotlin only compiles to Java byte code), standard Python (C Python) does not compile to native code at all. Go compiles very fast, there are other compromises coming with that.
I guess Swift compile times will become better (e.g. for macros and hopefully for large literal structures), but no matter how much better this will be in the future, a deployment method relying on fast compilation at the time of deployment on the one side and Swift on the other side will never be friends. Just donāt bring those two things together.
decades of evolution led Linux systems towards dynamic linking for a reason: dynamic linking uses less memory.
the Swift runtime is about 10x larger than the C runtime, and even bigger if youāre linking FoundationEssentials or the entire Foundation. if youāre copying that into every executable, thatās a lot of memory youāre wasting, memory that is not available for apps to do useful things.
one way to solve this issue is to deploy āmonolithā binaries that combine lots of apps into one executable. but that raises build-time costs as many have mentioned in this thread.
now, dynamic linking with Swift is a huge pain right now especially since it is ABI unstable. so in a lot of ways, monoliths are the path of least resistance for a lot of people right now. but it would make some things easier if we could deploy server apps individually instead of recompiling everything running on one type of host configuration as a unit. static linking is not a silver bullet cure-all solution.
We do actually use dynamic libraries on linux, but we can guarantee an exact same build and deployment environment and have to use a (minimally) patched swiftpm currently - far from ideal and not practical for most.
Some relevant open issues/pull requests for more generic linux support:
We use Swift on server to power a healthcare platform down in New Zealand. We chose it because we're mobile-first and unifying Swift on server and iOS seemed like a great choice 8 years agoā¦
Swift on server has come a long way but we still have major pain points:
The differences between macOS / Darwin / Xcode Foundation and Linux Foundation crop up bugs regularly that can be hard to track down and fix. It would be great if we had Linux Foundation linkable in Xcode (maybe this is possible now with the new FoundationEssentials?)
Lack of binary frameworks for Linux means no 3rd party libraries are vending xcframeworks which slows down development.
Development outside of Xcode is still lacking, VSCode with swift-lsp is improving but still not super ideal.
It may not help you in the short term depending on your required toolchain support version, but I can say that we went from a no-Foundation policy (for the reasons youāve encountered) to start using it freely as of Swift 6 - the new Foundation implementation which is shared across Darwin and Linux has been largely painless - but for the record we only use essentials.
can you provide a few examples of third party libraries you wish existed for Swift?
regarding tutorials: iāll also plug swiftonserver.com which was started this year and contains a wealth of examples for how to get started with Swift on the server.
Iām always interested in the ālack of 3rd party librariesā point because I feel most of the stuff I use from the js world is built into swift or hummingbird or vapor.
For me personally the only pain point would be fewer hosting platforms to choose from compared to node/express.
Another issue I can see is lack of corporate buy in and hurdles with using public GitHub from an enterprise environment. There just isnāt as much support in some workplaces to make swift as usable as a server language as Java, python, js, C# etc. I view this as a temporary problem that can be fixed with time and by improving the public perception of swift on the server.
Getting back on topic (sorry!) ā one pain point is, of course, binary size with the Swift runtime linked in. This has improved markedly with FoundationEssentials but there are still lots of packages that use Foundation. And auditing this can be difficult as itās linked implicitly (well, itās not that difficult, but you need to look at the symbols).
Yes, I was using āserverā as a proxy for embedded (but not Embedded). Many similar issues except size is also important. Although, ironically, on our device (which has multiple services that share the same package dependencies), being able to override to force them to build as shared libraries (type: .dynamic) would also be super-useful.
iām not sure where ābinary size doesnāt matter on the serverā came from, but i couldnāt disagree more.
on the server, binary size equals memory. if everything is statically linked, you use the entire size of the executable, eagerly at launch time.
letās say you have a host with 500 MB of memory (a t4g.nano). youāve got to leave at least 1/3 of that for the OS, and for headroom, which gives you ~300 MB for applications. if youāve got a 100 MB executable, that leaves you with just 200 MB of memory to do useful things.
well i guess thereās no clear line between embedded and non-embedded, but t4g.nanos are embedded than i guess i am an āembeddedā developer
folks are often amazed at what you can do with Swift on tiny machines. did you know the entire public-facing Swiftinit website runs on a t4g.micro with 1GB of memory? it co-habits with a MongoDB instance which gets 500MB of RAM, so the Swift app itself is only allocated 250MB of RAM to serve all of the siteās visitors.
of course the infrastructure is not just one t4g.micro, there is a second instance which replicates the data from the public-facing node (so we do not lose all the package documentation if one machine fails), and there are builder nodes that actually run the Swift compiler and build packages and upload the documentation to the micros. but those arenāt public-facing, and if they went away, the site would still be functional (for a while), the documentation would just get stale over time.
i think itās really cool how Swift is so memory-efficient that binary size becomes the new limiting factor. but static linking undercuts this huge selling point for the sake of ease of deployment, so i totally agree that this is not a globally-optimal direction for server-side Swift to be headed in.
Pretty much same issues for embedded. I have one board where ideally thereās just a 32-bit ARM and only a single Swift binary; static linking with no Foundation makes sense for this. The other is a Pi Compute Module, with a few different binaries (some of which need Foundation) and which have many dependencies in common, so shared libraries for all common dependencies would be preferred. I suppose the firmware build could identify the common dependencies and rewrite Package.swift.