Asking for your feedback on sbt + Scala Center announcement

These are the most significant issues I have with SBT:

  • In a large project with -30 modules, watching sources (eg. ~compile) even in just one of the modules often freezes for up to 20 seconds, doing nothing before responding to changes. Other tasks like testOnly can also suffer from this but usually with <10 sec of lag.
  • Sometimes an incremental build takes twice as long as a clean build. Hard to provide any info about when or why.
  • Scopes of keys are a nightmare. They’re great when you know exactly what’s needed but when you don’t know which scope you need it’s a nightmare to discover; similarly configuring a setting with the wrong scope never yields any warnings or errors. You just have to patiently run through all the related tasks and see if it works. Using inspect on tasks doesn’t help either because it’s usually 8 nodes away in the big task & setting DAG.
  • SBT doesn’t seem to care about UX. The UX is terrible across the board. When presenting compilation errors, it’s insanely hard to decipher huge blogs of monotonous text. SBT should use different colours for filename and line number at the very least. Even at the end it says “n errors” detected but if it’s ≤ 4 it says “four errors”, “three errors”. How ridiculous! This isn’t a damn novel. We aren’t reading all the output from top-to-bottom; we’re scanning for information we know is there. Now this is surely due to scalac not SBT but as far as I’m concerned SBT is the interactive tool here and it should be held accountable for it’s UI and UX; users new to Scala and SBT certainly aren’t going to bother with the distinction. Clippy is an SBT plugin that can be used to apply syntax highlighting to Scala snippets in compilation errors (still doesn’t colour filename and line numbers) but it’s 1) not enough, 2) should be part of SBT proper (or whichever SBT component does Scala compilation).
  • From what I can see on the outside, the SBT team seems too cautious, moving so slowly and scared to break anything for anyone (cough cough Ivy). SBT isn’t solid enough or well designed enough to improve in a reasonable timeframe with tiny, safe changes (which is probably why CBT has surfaced and is gaining momentum). When you look at all the deficiencies and desired features and ask how long it will take to fix/add, at the current rate it will take another 5 years or more. Are people really going to stick around? A strong backwards compatibility story won’t be worth much if, for example, 80% of the community abandons the tool.

Also worth mentioning:

  • It unclear how loose settings floating around in the build.sbt DSL apply in a multi-module project. The whole DSL/Scala dichotomy really feels like a big mess. Why not just Scala? It’s just adding complexity and knowledge curve for no benefit as far as I can tell.

I’ve also good to say about SBT. It used to be hell 4, 5 years ago but these days, with all the improvements and simplifications that’ve been made over the years, it’s a really good tool. It’s trivial to add new modules to a project, to have interdependencies between them, using the .value macros it’s super simple now to customise builds and add task dependencies, it’s not a big deal to add custom commands or settings. I think SBT has a lot going for it and, while I really wish the rate of improvement was faster, I do appreciate the work that has been done to improve it from the monster it was years ago.

4 Likes

Since this is (or at least was at some point) a conscious decision and considered a feature, I haven’t submitted a PR to change it, but I agree with the sentiment. Using digits in all cases would make it easier to locate the information on the screen.

Here are my thoughts about SBT:

  • SBT API is very complex and contains a lot of unnecessary stuff. I believe public API should be as simple as possible. It should be reduced to a few basic conceptions (project, task, setting, key) represented by a few classes with a few methods. Do we really need Def or Initialize in the public API? Recent changes (like replacing symbolic operators with := and .value) is pretty good, but I think there are a lot of places where we could make API simpler.
  • Scaladoc. There are a lot of code inside SBT without comments, including some widely used APIs.
  • Type safety issues. For example, absence of .value produce runtime error, but it should be compile time error IMO.
  • Too much macro magic. It’s almost impossible for a newcomer to understand what’s going on inside SBT.

Some points are already discussed above, so I will not repeat them. Personally, I think that SBT is moving in the right way, but this moving is too slow.

1 Like

I would love to see this discussion move back to “immediately” actionable request for SBT improvements.

The discussion about conceptual issues is legitimate, but I think it should be part of a different thread.

6 Likes

it is super-hard to understand find and use settings/keys/scopes correctly without investing huge amount of time.
IntelliJ helps a bit, but not that much.
Problem gets even bigger if one wants to use few plugins.
some examples:

  • how to inspect flags (-Dxxx) passed to test in subproject ?
  • how to execute task in subproject ?
  • where should I put “my” settings - like ( setting in Scope) - what Scope should i use? how to check It, do I have to think about it?
    all such little things requires thinking and remembering details, which even in medium projects can be tedious

Compared to maven which I used before I think it is kind of problem of power and flexibility. Sbt is very powerful and flexible compared to maven, but taming that power requires serious investment of effort.
Maven on the other hand is much more limited, anything non-trivial requires a plugin. and even plugins don’t have the same flexibility that sbt offers out of box via settings/keys/scopes.

Overall, I would say I am really happy with sbt. I think it’s the best build tool available for the JVM; if I was for some reason to start a Java project, I would probably write its build in sbt.

I like the way that settings work and that you can explore their interdependencies so well with inspect. It’s nice that you can just guess at scoping, and get compiler or setting-solver errors if you screwed it up. Where inspect or show failed, I’ve found it easy enough to browse Keys.scala and Defaults.scala to figure out how things are being computed and where the correct place to insert a customization is.

It’s also wonderful that so many things are type-checked, and if not that caught by the setting interdependency solver. Having to wait until you’re trying to release to get basic errors that could have been caught with types is really annoying; sbt heads that off pretty well. It also makes it even faster to do exotic things, since you don’t even have to run any builds to catch most errors. I don’t really have any strong feelings about DSL versus regular Scala source; they are both fine to me.

I also had a pretty easy time working out how to publish to Sonatype, and doing so, just following the manual. This was back before sbt-sonatype existed, even, so it is probably easier now. Maybe not having trouble with this makes me an outlier.

In particular, we would like to listen to your ideas and suggestions to improve the build tool. How could we make sbt easier for your day-to-day job? What current interactions with the tool you would like to see optimized?

My only real ask would be a built-in version of sbt-adhoc-subprojects-plugin’s core feature that actually worked with normal projects. Being able to treat whole other projects as subprojects is really nice; I used it all the time with Leiningen when I used Clojure. My hacky plugin is not nearly good enough for real library sbt builds, though.

My experience may be atypical, however. Maybe most users do not care about patching libraries while working downstream.

4 Likes

Hello, I think this is a great initiative.

I’ve been using SBT professionally but never learned it. I think

  • It has a complicated syntax
  • Too many files
  • It’s really hard to find documentation of the simplest things for your version. There are so much weird documentation which mixes Build.sbt and Build.scala (I think)
  • Eclipse have trouble using SBT (last time I checked)

The only thing I want is

  • Simple format, JSON?
  • Work with Eclipse
  • Set meta data and dependencies, and then use IDE to make full project setup. Automatically download dependencies is the main reason I use a building tool
1 Like

I agree with that point, the syntax makes it hard to learn it. I love sbt for one specific task. Creating an Uber jar for my Spark project with the assembly plugin.

Moving files around to be able to create the final deployment package shoud be trivial.

It would be nice to be able to use regular Scala to perform simple task without having to google it.

For me the most awaited feature of sbt-1.0 is the sbt server. Is there any documentation or example code of integration with it?

Not sure if this is something I can fix myself, but after using sbt a while I get:

java.lang.OutOfMemoryError: Metaspace

Which requires me to restart SBT

1 Like

I can only echo many of the points that have been put forward in this thread already. Please take the criticisms here with a grain of salt because I think SBT is a great tool, I just use it all the time and encounter many of the rougher edges.

Pros:

  • It’s still (probably) the best build tool I have used on the JVM
  • The user interface used by SBT - an interactive shell which covers most common tasks - is great
  • For trivial projects it’s brilliant - set a couple of keys, put your source code in the right place and off you go

Cons:

  • Beyond the most trivial projects, it’s incredibly hard to understand for beginners due to the amount of magic involved
  • Has a strange DSL that blows away all your intuitions about how Scala code works - e.g. if (bool) task1.value else task2.value always evaluates both tasks
  • Documented for somebody who already understands SBT
  • Documentation steers people toward overly complicated usage of SBT (e.g. multi-project builds when they’re not necessary) even for trivial projects
  • Requires odd file layouts that are hard to explain to beginners, e.g. why do I need to add plugins to my build in a different directory to my build file? Why is the version of SBT set in a properties file when everything else is in the build?
  • Related: the recursive build thing is cute but it’s basically just more boilerplate to maintain that I would rather was in the build.sbt or supporting .scala files
  • Incredibly resource-intensive
  • Even slower on Windows which is unfortunately ubiquitous
  • Really slow dependency resolution that makes you regret using multiple projects and configurations
  • Requires detailed understanding of its inner workings to achieve anything that falls outside its built-in functionality
  • The above is exacerbated by the poorly documented source code and dense, difficult to understand APIs
  • The reliance on plugins that are not maintained by the SBT team for functionality that is almost necessary due to SBT’s complexity (e.g. sbt-dependency-graph, sbt-release) is very awkward
  • I have inherited a build that uses multiple configurations to enforce a layered architecture and getting many plugins to work is a terrible experience akin to a Satanic ritual. I have given up on getting Scoverage to work, for example.
  • Doing anything non-trivial with artifact publishing like creating a .zip bundle of several projects is really difficult and I usually give up and use sbt.IO to do it myself because at least I can understand that

I frequently find myself staring at a terse, one-sentence explanation of a key in SBT’s source code trying to make sense of it when I am trying to understand an obscure error. Often the sentence essentially repeats the name of the key in long-form.

I have been forced to add a section to our onboarding page for Scala developers at work describing how to set JVM options to increase the amount of memory allocated to SBT using environment variables. It would be great if this could be configured in a less annoying place and if it had more realistic defaults given how terribly resource-intensive SBT is for multi-module projects.

It’s baffling to me that SBT continues to use Ivy even though it’s really slow, has a global lock that makes it very difficult to use in CI without simply using a different Ivy home for each build and has lots of outstanding bugs that have not been addressed for many years (like the one where it fails to publish large artifacts because it keeps the whole artifact in memory).

Overall I get the impression that SBT was designed to demonstrate an idea for a clever task engine and day to day usage of SBT was not considered until much later when it was a huge, out-of-control project.

Everything about SBT has improved substantially since I first started using it but I feel there is still a long way to go and I frequently think about the simplicity of CBT enviously while maintaining our build.

3 Likes

I mostly agree with what has been said but I wanted to bring a small light of hope on a couple points made by DavidGregory084 :slight_smile:

That’s really sad becasue this is mostly a documentation issue : you can create a .sbtopts file at the root of your project for project specific sbt launch options including JVM configuration. I was unable to find a mention of this in the official sbt documentation. It is mentionned when you run sbt -help however.
This feature was added when the official sbt run script adopted sbt-extras. It seems the official script lives at sbt-launcher-package/src/universal/bin/sbt at master · sbt/sbt-launcher-package · GitHub

You can find mention of this feature here and there on the internet.

I have yet to find an non contrived use case for that.
However I find it very easy to explain why you have to declare plugins in a specific directory at the top level.

Because at the moment you can only have one version of a plugin in a build. If you were able to define the plugins directly in a module’s build file, it would become very easy to declare different versions of plugins in two different modules within a multi module build. How would that behave ?
Maven actually had this issue if you don’t use pluginManagement and used to more or less randomly select a version of the plugin (it would select the version of the first module built and use that for all other modules regardless of the declared version, imagine how easy this can be to understand the first time it hits you) not sure if it has been fixed or not.

So I do like that some settings are for the whole build (such as plugin definitions) and some settings are for each modules.

1 Like

to 2) found minimal .gitignore for a single build project at https://github.com/typesafehub/activator-minimal-scala/blob/master/.gitignore
it contains the details in directory project

@Fristi Do you add this flag -XX:MaxMetaspaceSize= to your sbt? If you don’t add this flag, I thought it would be hard to solve by yourself.

Jumping into the discussion: I do have that flag set. Doesn’t help – I still get Metaspace OutOfMemoryErrors after running my application a few times. This has been going on for years now, and I have yet to figure out how to fix it…

(I should note that I don’t know if the issue is sbt, Play, Akka or what – it’s a somewhat complex application.)

  1. Instant start: Interactive is terrible from a UX perspective and not at a solution at all, some times, because of the Metaspace OutOfMemory errors bug.
  2. Improve documentation. Also “Documentation steers people toward overly complicated usage of SBT”.
  3. Standardize SBT configuration: Projects tend to use SBT in totally different ways. Good boilerplate, good documentation and a list of best practices list.
  4. Simplify syntax: Learning SBT is like learning a new language, but it should feel just like using normal Scala.

That’s really sad becasue this is mostly a documentation issue : you can create a .sbtopts file at the root of your project for project specific sbt launch options including JVM configuration. I was unable to find a mention of this in the official sbt documentation. It is mentionned when you run sbt -help however.

I’m aware of .sbtopts, however as far as I know it doesn’t work with the Windows launcher script, and all of my devs are on Windows :cry:

Because at the moment you can only have one version of a plugin in a build. If you were able to define the plugins directly in a module’s build file, it would become very easy to declare different versions of plugins in two different modules within a multi module build. How would that behave ?

Couldn’t plugin definitions just be prohibited from being scoped any more tightly than in ThisBuild?

Jumping into the discussion: I do have that flag set. Doesn’t help – I still get Metaspace OutOfMemoryErrors after running my application a few times. This has been going on for years now, and I have yet to figure out how to fix it…

As far as I know -XX:MaxMetaspaceSize is actively unhelpful - if you look at the documentation it states the following:

Sets the maximum amount of native memory that can be allocated for class metadata. By default, the size is not limited.

By using this option you are actually imposing a limit that was not there before - just delete that option :slight_smile:

1 Like

For the record this is what I recommend to the guys at work:

Create a new environment variable SBT_OPTS in Control Panel → System → Advanced system settings → Environment Variables.
Set the value of this environment variable to something similar to the following:
-Xms1G -Xmx4G
There are other options which may prevent SBT from running out of memory:
-XX:+UseG1GC - Enable the garbage-first (G1) garbage collector. This will be the default as of Java 9.
-XX:+UseStringDeduplication - Reduces the memory footprint of a running application by deduplicating identical Strings. In order to use this option you must add -XX:+UseG1GC first as it is this garbage collector which performs the deduplication.
-XX:-UseGCOverheadLimit - Enables the VM to spend longer in garbage collection (GC) before throwing an error. This may prevent SBT from throwing OutOfMemoryError: GC overhead limit exceeded, however this may come at the expense of extremely poor performance. It’s probably better to ask for more memory than to enable this!

Reading through most of the recommendations I see about SBT options (on e.g. StackOverflow), I usually find that they are already the default values as of Java 8.

I’ve already replied further up, but I’ve been thinking about a few more things which would benefit from attention post 1.0.

  • unify commands / tasks / sbt-release “steps”
    – These are three different ways of approaching the same problem (chaining sequences of steps together). I think that in sbt 2.0 only one of these three (probably task) should remain.
  • get rid of “.sbt” files and macros.
    – I don’t think either works the way that it was intended. They make things easier for a total beginner but complicate the life of everyone else by having so many frustrating corner cases and by being their own badly documented DSL on top of standard scala.
  • migrate every custom sbt library to a standard community equivalent.
    – primarily, i think this would mean make much heavier use of cats.
  • make reload faster.
    – the feedback look on trying to make changes to an sbt project is necessarily slow because of scala compilation, but i think sbt adds additional overhead that frustrates people trying to get something to work.