Probably naive question (not 100% sure how SwiftNIO works under hood, need to check finally), but is there a reason not to make this single process isolated? When it crashes then SwiftNIO can inform whoever and recreate to process.
SwiftNIO not (yet) running on Windows is an issue. Of course, āa real server application runs on Linuxā, but you might want to run a server application locally on your machine, be it for development or deployment.
Here are some of the pain points I've hit recently for Swift on the server:
FoundationEssentials - my current hill to die on . Trying to use this when developing on macOS and deploying on Linux is a case of trial and error and really sours the experience when trying to adopt new Foundation and remove binary sizes
Linking issues - this has been a perpetual issue in Swift for some years and improvements were made back in 5.9/5.10 but do appeared to have regressed in Swift 6. The linker is extremely memory hungry and makes it hard to build Swift applications on memory constrained environments. Especially Heroku build packs, and those developing on an old-school approach for a VPS which both compiles and hosts the application. It would be great to have these regressions investigated and fixed
General build times. Swift is always going to struggle to compete against interpreted languages when it comes to build times but comparisons to other compiled languages like Go are pretty stark. I think we could do better as a community at offering solutions for SwiftPM caches in CI etc and it would be great to see things like dependency caching be a thing.
Formatting stack traces - systems like Google Stackdriver logging have strict requirements in the format of logs. Before backtrace was built in we could attach signal handlers, manually capture a backtrace, log it however we want and then crash out. This is no longer possible and the backtrace runtime currently doesn't allow us to format the logs. This is a pretty minor issue however, just one I've hit recently in an app migration.
Personally I've had very few issues with applications crashing in live (part of the reason why I'm not hugely concerned with the last point). Especially since we've adopted Sendable there have been a marked drop in reported crashes and generally things are pretty stable.
Presumably because that is VSCode?
But that's actually besides the point, there should be no stark differences between the platforms like that. I think it is fair to assume that Swift server code on macOS should work pretty much exactly the same as on Linux (there are of course some internal differences between BSD and Linux, but nothing which should affect Foundation in significant ways?)
It's a reason why Mac's became so popular for server side development in the 200x, you'd have a proper Unix that could run the same software as on Linux w/o noticeable differences, but you also get an actually neat UI.
That is our experience and it works pretty well now after the foundation merge in 6.0. The major difference which can cause problems in our experience is the slightly subpar implementation of the concurrency runtime for lib dispatch on Linux compared to macOS. Hopefully weāll see closer parity there in the future.
One of the big core platforms differences is that everything is AnyObject on Darwin due to the bridging. (in fact it is a little disappointing that we don't have ObjC support on Linux, would be quite useful to me )
I'll actually add FoundationXML and FoundationNetworking to this list. It's easier than essentials since the boundary is well understood and defined, but it's still confusing to new comers to the language and would be great to include
Using Swift on the server is only possible when your server platform of choice is supported by Swift. The supported platforms are Linux, Windows and macOS (theoretically it is a server OS too). People who love and trust other highly-capable server operating systems like FreeBSD can choose between:
@doctorj I hope Iām not too late, but you know holidays. Iāve been running several Swift on the server components and here are my top 4:
Build times, compute and memory footprint during build are astronomical. Swift components are costing us 3x more than JVM/Kotlin components and 10x more than Rust/Go components to build. Itās really quite expensive to run Swift on the server.
Limited supported platforms e.g. what about Debian or BSD or Fedora etc. Itās also misleading that development and production platforms are not the same. Creating custom Docker images is not easy with dependencies, build times, and the resulting image sizes are gigantic - we actually pay for storage and egress for our container repositories.
Developing for the server on macOS is a huge pain in two main areas: 1) the overlapping types with iOS/macOS frameworks (e.g. Crypto) makes it impossible to know if what builds on macOS will build and even run on some Linux environment. Others have already mentioned the pain of FoundationEssentials, networking etc. 2) As a result of the previous, the ecosystem of Swift packages is full of libraries which although claiming support for server/linux targets, do not actually (correctly) support that.
DX around tooling has many papercuts (we see this when onboarding devs coming from more established server language ecosystems). One very prominent point is, well, IDE - Xcode is great for apps but positively hostile for server apps. There is VSCode but beyond that itās hard to configure proper language server support (e.g. say one is using Zed, or vim/emacs inspired setup), get hints about supported targets for specific functions, running tests, visualising test results in existing systems, integrating with vulnerability/security scanning tools etc.
I find that one interesting, I would assume that Rust (not Go or Java/Kotlin) is in the same compilation-time territory as Swift, i.e. comparatively (very) slow to compile.
It is more expensive to build for the server, but in my experience it is much less expensive to run Swift on the server, in particular compared to the JVM. Presumably that's kinda the idea/goal.
Do you really spend more resources in building things vs actually running the results?
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
Really? I'm currently porting OpenGroupware Java to Swift. Java compiles are literally instant and happen on save in my Eclipse setup. The speed is unsurprising given how simple the language is and how much work is postponed to the JIT. It really is day and night. The iteration cycle in server Swift is an issue you don't have in most other mainstream languages, especially if you link a lot of the extra server packages or just things like Crypto (really, I want the system libssl, not an own copy I have to build every time and that comes w/o an SLA).
The one that is off here is Rust which I would assume is close to Swift, but @hbko mentioned on Masto that he thinks that the Cargo ecosystem is much better than SPM wrt fragments and such.
That's what I do as well, but he is still correct, it is hostile to server apps. No remote deployment or debugging, which Xcode does out of the box for Apple platforms.
(I think Apple wouldn't even have to do this, what Xcode is really lacking is extensibility)
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.