Swift Regrets: Wrap-up

Thus ends five months of Swift regrets (and delights), things that I had been collecting during my last year at Apple…and things that came up during the ensuing discussion on Twitter. I wanted to talk about these things because every project learns from those that came before it, and that should include both the good parts and the bad parts. I have a memory of my former colleague Joe Groff saying that we should normalize and encourage talking about mistakes and missteps in our field, and so this is a contribution.

All of the Twitter threads have been collected on this site under the “Swift regrets” tag. It’s not an exhaustive list of all things good or bad about Swift, but it does span quite a range. My personal favorite is probably one of the first ones I did, Sequence. It lays out the problem and the possible solution well—in my opinion, of course.

So what are the takeaways from this series? Besides “<feature> was a mistake, think twice about including a similar feature in your language”, I think we can probably come up with a few high-level ideas, the most important being that at some point in language work you don’t get to change things anymore! This is a limitation that just doesn’t apply to the majority of software projects, just (widely-adopted) languages, libraries with ABI stability, and interchange formats. Everywhere else you can introduce a new major version that replaces the old one and people will either move over or not—it might be difficult but it’s always an option. I think this is a culture shock for people coming from most open source projects, or straight-up app projects. (And I’m saying all this knowing the massive number of changes from Swift 1b1 to Swift 3, so to think there were things that still didn’t / couldn’t get fixed…)

That’s the most important thing. Some more regrets-related advice:

  • It’s easier to add capabilities or remove restrictions than the reverse. People will immediately start using any features provided, especially if they’re the only way to accomplish a task, and even if you introduce a universally acclaimed better feature later, removing the old one becomes a breaking change.

  • If you’re going to make breaking changes, make them boldly, because you probably won’t get another chance.

  • Make your most ambitious changes early, so you can try them out and tweak them…and revert them if necessary.

  • That said, don’t add anything that seems “cool” or “clever” or even “obvious” unless you can cite real-world uses. Otherwise, it might be a trap, or extra complexity, or at the very least something else to maintain forever.

  • Relatedly, if something’s not supported in another language with a similar feature, maybe there’s a reason.

  • Implicit can be convenient, but it may also be a bad default.

  • Consistency is good: less implementation and less to learn.

  • But unrelated features can also interact in weird ways.

  • Getting to go back and redo something properly is rare; your first version better be good enough to last forever even if you want to go back and fix it.

  • Relatedly: perfect is the enemy of the good and good is the enemy of the perfect. Yes, it’s a problem in both directions.

  • But sometimes you just won’t know you’ve built the wrong thing until you’ve seen it used. And sometimes there’s not obviously a right answer.

On the “delight” side, I think it’s less obvious what might tie everything together, but here are a few thoughts:

  • Some of my favorite things about Swift are tweaks on what other languages already had. Sometimes the best thing you can do isn’t create from scratch but polish and make accessible…or straight-up do the same thing as someone else, if it’s the right thing. (That’s a very Apple approach.)

  • Similarly, you may not be able to break compatibility yourself, but you can find all the places other people wish they could break compatibility and follow that.

  • All the usual pithy quotes like “easy things should be easy and hard things possible”, “API should steer you towards correct use”, “clarity over brevity”, etc.

Given all this, I should probably again answer the question of “isn’t it worth breaking compatibility if it makes things better?” When you’re talking about a programming language, the old version doesn’t go away, doubly so if you can import an old-version library at compile time and triply so with ABI stability on Apple platforms. Breaking compatibility means alienating your users or maintaining multiple versions for years to come, or more likely both, so the changes had better be worth it. (There is a scale question here too: there are millions of developers for Apple platforms and in turn probably a billion users.)

I should also repeat that the takeaway is not “Swift is a failure, look at all these things wrong with it”. All languages have lists like this; the reason I did this for Swift is because I worked on Swift and can provide insight as to how and why things ended up the way they did. I’d love to see posts and threads from other language designers about what they regret and what’s held up well.

The series has generated a lot of good discussion, and in particular I want to give a shout-out to Dave Zarzycki, who also worked on early Swift as well as a number of other projects in his many years at Apple (including the original launchd). In one of our private conversations he came up with this, talking about a language that’ll last for years and be suitable for large collaborative projects:

Assume success and solve the people scaling problems as soon as possible. That’s it.

That being said, it’s a massive amount of work. It means getting a ton of defaults right, thinking hard about features interact with each other, progressive disclosure, planning for API evolution and resiliency etc, etc.

More so, it means foregoing some of the traps-of-convenience that new PLs are often tempted by (like haphazard implicit conversions)

And that all seems like good advice to me.

If you’re looking for more language design retrospection, I suggest Joe Duffy’s series on Midori, a Microsoft language and operating system project that started a few years before Swift did. The project was eventually discontinued and the developers folded back into other parts of Microsoft, but its aims were even more ambitious than Swift’s, and there are both some striking similarities and some things we could have learned from.

Thanks for following along these five months. See you in 2022.