authors/asterite.jpg authors/bcardiff.jpg authors/waj.jpg authors/450.jpg Ary Borenzweig Brian J. Cardiff Juan Wajnerman +450 more 2 days ago

Crystal 1.0 - What to expect

The release of the first major release of Crystal arrives after many years of hard work. With thousands of contributions from people worldwide, it was finally possible to find consensus for what truly mattered for 1.0 and what could wait for future releases. Getting here wasn’t an easy journey, filled with enriching, controversial, delightful, and endless conversations that, in the end, made it possible to build a language more useful for more users.

But what does it mean to have a 1.0 version? After all, the process of receiving valuable contributions and evolving the language will not stop after this milestone. Let’s dig deeper to understand the true meaning of this release for the community, especially those already using Crystal in production environments.

Language Stability

Language stability is probably one of the most prominent and expected reasons to have Crystal 1.0 finally. We received numerous comments from people willing to use the language in production during all these years, but only after we could make a promise of not breaking it from one day to the next.

After this release, everyone can expect that, at least for any future 1.x version, your code can still compile and work without any significant incompatibility. Language and standard library features won’t be removed or changed in any way that could prevent existing code from compiling and working. The built-in standard library will continue to be enriched but always with backward compatibility in mind.

Nobody can stop this outstanding community from having groundbreaking ideas every day. But those contributions now belong to a separate branch where the vision for Crystal 2.0 will start to sprout and grow every day.

Release Plan

To increase the project’s predictability, we plan to release maintenance releases (aka bug fixes) as patch versions. For example, the first maintenance release will be 1.0.1, and it will only include fixes of existing features.

New features will be reserved for the subsequent minor releases: 1.x. Again, always maintaining backward compatibility with previous versions.

How many 1.x versions are going to be maintained in parallel? We don’t know for sure yet. That will depend on several factors, including the availability of core team members. For now, we envision maintaining at least two minor versions. That means a 1.0.x might be released even if 1.1.0 is published.

Because of the “no breaking changes” rule between 1.x → 1.x+1, migration to new minor releases should be relatively easy, with migration plans to be eventually released along with new versions.

Preliminary Features

There are some features currently available in Crystal that didn’t receive official support yet. We plan to keep improving them until we’re confident about their stability. These features will continue to be available on every version, and you can expect an official blessing in a future 1.x release.

Windows Support

Tremendous progress has been made to make Crystal available on Windows platforms. But there is still tons of work to be done. However, none of that work should impact the semantics or libraries available for *NIX platforms. So we decided not to delay 1.0 because of incompleteness in this area.

We keep encouraging the Windows community to continue participating in the support for Crystal in this environment.


Support to run the Crystal runtime on multiple cores is still a preliminary feature available through the use of the -Dpreview_mt flag. But there are still many improvements yet to be made to guarantee the language’s expected performance and quality in a multi-core environment.

And it’s not only a flag to be enabled to let existing code run in parallel. Idioms and guidelines must be written to ensure a sane migration into a multi-core environment. More of this can be expected in future 1.x releases.


Porting Crystal into ARM platforms was always important, mainly for embedded and mobile devices. Who didn’t dream about writing applications for iOS and Android one day in this beautiful language? However, recent announcements from Apple to use ARM in computers made this platform even more relevant for the language.

Significant steps have been taken to improve Crystal code’s cross-compilation, and even our CI runs most of the Crystal specs for every commit on ARM servers! However, some work is still pending to improve the experience for cross-compilation, and even some core libraries need work before we can be confident to give the “production-ready” mark on ARM machines.

Changes this release

The changes that landed in 1.0.0 since 0.36.1 were focused on polishing, but it is worth mentioning some of them here as a migration process. Don’t miss out on the rest of the release changelog.

There were 82 commits since 0.36.1 by 25 contributors.

If we count them since the project inception, there were +13000 commits by +450 contributors, +9000 issues/PRs, and, more importantly, a great community that is sharing experiences, ideas and dreams.

Language changes

Tuples allow type-safe accessors with literal indices since the compiler knows which type each component is. In #10379 this knowledge is also available for literal ranges.

tuple = {1, "hello", 'x'}
v = tuple[0..1] # => {1, "hello"}
typeof(v)       # => Tuple(Int32, String)

String and character literals are no longer allowed to have unicode surrogate halves in escape sequences. You can express arbitrary values using \x still, so "\uD800" should be "\xED\xA0\x80". Read more at #10443.

Standard library

The std-lib got rid of most of the deprecated definitions present until 0.36.1 in #10386. This includes dropping:

  • Spec should, should_not overload
  • Int, String, Time overloads with trailing IO parameter
  • Set methods
  • Dir.rmdir, File::Info#owner, File::Info#group
  • HTTP::Request, HTTP::WebSocket, HTTP::LogHandler methods
  • URI#full_path method
  • Time::Span#duration method
  • StaticArray#[]= method
  • Hash#delete_if method
  • Process#kill method
  • OptionParser.parse! method
  • Log.setup_from_env overload
  • has_attribute? macro


The getter and property macros will add the return type annotation when possible. While adding this lazy initialized property no longer accepts nil as a valid value. It’s confusing and you probably were not relying on that. Read more at #10405.


There were some iterations to add more rounding options to Number and match the functionality available in other languages. Number#round accepts a RoundingMode parameter to choose the exact criteria to use. There are new methods like Number#round_even, Number#round_away if you prefer them. The default rounding was changed to round_even (a.k.a.: round nearest ties even) matching what other libc, Java, Julia and IEEE-754 recommendation.

The new available rounding modes are:

  • TIES_EVEN: Rounds towards the nearest integer. If both neighboring integers are equidistant, rounds towards the even neighbor (Banker’s rounding).
  • TIES_AWAY: Rounds towards the nearest integer. If both neighboring integers are equidistant, rounds away from zero.
  • TO_ZERO: Rounds towards zero (truncate).
  • TO_POSITIVE: Rounds towards positive infinity (ceil).
  • TO_NEGATIVE: Rounds towards negative infinity (floor).

Read more at #10413 and #10508.

Downcasting a float with infinity, or converting an out-of-range float has a more accurate behavior. Read more at #10420.


Enumerable#flat_map, Iterator#flat_map works now with mixed element types. As a general guideline, when dealing with multiple types of elements, check your design. You might be translating code from other languages where that is common, but in Crystal, it might not be the tidiest thing to do. Either way, in #10329 there is room for improvement on how things are done in the std-lib to allow more and more use cases while using the existing language features.


Enum serialization is now done as underscored string representation. If you want the old behavior, use the Enum::ValueConverter. Using the underscore string representation is more stable if enum members are added, so we encourage it as a good default. Read more at #10431.

The types defined in the XML module were changed from struct to classes to allow future improvements on how the GC interacts with libxml. Read more at #10436.


There are a couple of changes regarding how cookies are handled. In #10486 the HTTP::Cookies.from_headers method is split into separate server and client alternatives. And the cookie name and values are no longer encoded/decoded due to security reasons. Read more at #10442 and #10485. It is left to the framework and application to choose if there is a default encoding for cookies.

Next steps

This release is available in Bintray’s apt and rpm repositories post, as well as GitHub release artifacts. The sunsetting of Bintray took us by surprise. We will need to make another move to change the repository location. Luckily we learned enough from the previous process, and not everything is lost. We will attempt to keep the installation script API stable.

We have been able to do all of this thanks to the continued support of 84codes, Nikola Motor Company and every other sponsor. To maintain and increase the development pace, donations and sponsorships are essential. OpenCollective is available for that. Reach out to if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!