Tuesday, October 3, 2017

Why Jenkins is a wrong CI tool

Jenkins is one of the most awkward tools I ever met outside of Windows domain (in the Windows world they are the norm). I would say that this favorite tool of Devops engineers reflects the identity problems of the Devops engineers themselves. Basically, Jenkins is a build automation tool built over some other build automation tools, like Maven or Gradle. If you ask me, you should avoid using it like fire, so many problems it brings. Here are some of the flaws of Jenkins (or what I consider as flaws), some of them innate, others acquired later, in no particular order.

  1. Plugins may be a good thing when all you need is to add a couple or two of features. Jenkins is nearly useless without plugins. You'll need dozens of them, if not hundreds. Is it bad? It is. Plugins make every Jenkins server a snowflake — unique and unreproducible. The modern trend for automation stalls when it meets Jenkins. Jenkins cannot be presented as a code, you can't automate the rollout of a new build server with all the plugins you will need.
  2. Plugins may be incompatible. If all those features were built into Jenkins, the incompatibilities could have been identified during the testing phase, but they aren't. Have you ever tried to clone a Git repo and its submodules while using a multibranch pipeline? No way.
  3. Inconsistent GUI. More than once I saw a situation where a Jenkins job copied physically to another server looks differently because the plugins were of different versions or they were installed in another order or whatever. The order of boxes in the pre-build section could be completely different, making the copy of the job unrecognizable.
  4. While we are at it, the Jenkins' GUI is a typical example of the "Made by developers"™ approach, meaning it's just disgusting (no offense meant for the developers here :). See "This Is What Happens When You Let Developers Create UI" for the details. There's an attempt to redesign the Jenkins' GUI called Blue Ocean, but it's just a superficial change.
  5. Groovy as a scripting language is a pretty strange choice. Scripting languages are supposed to hide implementation details and abstract certain operations. Groovy scripts in Jenkins, on the contrary, require extensive knowledge of Jenkins internals: class names, methods and so on.
  6. When you first see a working Jenkins server, it's nothing but an alphabetic list of jobs. Some of them are only called by other jobs, others may be never used remnants of the past, while still others are real top-level jobs triggered either manually or by a scheduler. The only way to understand what is what, which job call which jobs which call some other jobs and in what order, is to physically browse all of them and sketch out a graph on a piece of paper. It may take more than one day to finally grasp the web of their interdependencies.
  7. In a similar vein, the parameters for the jobs can be defined manually, calculated by a script, passed from another job, inherited from a parent job,etc. Together with the graph of the calls you'll have to compile a list of parameters and their possible values. Once again, use your eyes to find them all by skimming over all the jobs.
  8. When you're trying to build a new job, there's no way to test it. You can only run the job on the server and hope for the best. It's one more obstacle on the road to CI as a code.
  9. There are too many ways to write a job. You can make a vanilla freestyle job out of ready-made Lego-like blocks. Or you can write a freestyle job that builds the project by running a shell (or Groovy, or Python) script. Or you can create a pipeline. But you'd better watch out, because there are two different ways to write a pipeline, a scripting syntax and a declarative one. Or you can put your premade pipeline to a file called Jenkinsfile and store it with your code, Jenkins will create a job from Jenkinsfile automatically. There's more than one way to do it in Perl, too, but in Perl it's still the same language. In Jenkins, two jobs can be two completely different build systems.
  10. The changes in the build process always depend on the changes in code. Hence, the jobs evolve just like the project itself, but those changes are not kept anywhere in Jenkins. Of course, "there's a plugin for that"©, but that plugin doesn't keep the correspondence between the state of the project and the job itself, as a version control system could.

The last point is mitigated by the concept of Jenkinsfile, but it's not a silver bullet, either. Not all plugins can be used in Jenkinsfile. Not all libraries can be called from Jenkinsfile. It runs in a sandboxed Groovy. AFAIK, Jenkinsfile doesn't support post-build steps. And so on, so on.

Okay, but how a better CI tool should work like? I'm not sure, I haven't seen one. First, it should be more like a DSL, kept together with the source code. The DSL may be based on a well-known scripting language, like Tcl, Scheme or Python. Second, the build process used by the tool should be easily reproducible by developers locally. Third, the build results may be represented in HTML, but the web interface should be clearly separated from the build process itself. It may be too long to keep counting, it's a topic for another article. But for now, I'm not yet aware of a tool like that, but the time is nigh. Someone should write it. I just can't wait to ditch Jenkins for something better.