Opinion

The last few years saw the emergence of the MLOps practices, as companies started to realize that it takes more than a machine learning (ML) model to make a ML project. Sometimes, these practices like ensuring reproducibility, monitoring and governance can be more complex than creating the model. From a high level, MLOps aims to smoothen the transition from the development phase to production. The success of this transition is usually coupled with the quality of the source code. So, it’s important to carefully choose the tools that help you produce robust software.
Jupyter Notebook remains the tool of choice for many Data Science teams. It’s used for simple data exploration but also for creating data processing pipelines and model training. Some cloud solutions even let you deploy notebooks to a production environment. I agree that it’s a great tool for exploring a new dataset, analyzing results and creating and interacting with plots more easily. However, in this post, I will try to explain why Jupyter Notebook isn’t the right tool for you if you aim to build quality software.
It Encourages Bad Coding Habits
One of the software best practices is to split source code into multiple files. This makes the code easier to work with, debug and maintain. However, since importing a notebook into another is very limited, the entire code usually ends up in a single notebook. This promotes code duplication, making the code base hard to maintain.
Also, instead of defining classes that have small functions with clear names and single responsibilities, the code is arranged into cells. This leads to multiple issues. First, during the lifetime of the notebook, either the cells become very long or the same piece of logic gets spread across multiple cells. For example, you will find a preprocessing logic handled in cell numbers 5, 10 and 17. Second, testing becomes either limited or pretty much impossible. In some cases, the test consists of running the entire notebook where no exception means a successful test. Other cases use some primitive pytest ports, where the notebook needs to include the tests.
Finally, the code generating the plots is usually highly coupled with the rest of the code base. This coupling makes it hard to understand the code and making updates.
While I only touched the surface of the potential issues, it’s easy to see that this can lead to serious long-term problems. First, since maintenance makes more than 70% of a software project cost, making the code hard to maintain will increase this percentage or in worst cases lead to a complete rewrite. Second, if your first exposure to writing source code is in notebooks, it will be very hard to change these bad habits later.
Version Control Is a Nightmare
We all agree that version control is mandatory especially for large teams. So, the tool you’re using shouldn’t be in the way of properly versioning your project, and this isn’t the case for Jupyter Notebook. Under the hood, notebooks are large JSON files that include the source code, plots data and Jupyter metadata. This makes version control a real nightmare because:
- Checking the difference between two versions of the same document is hard. You can’t rely on github to do your pull request (PR), you must try to find a third party library that "tries" to do that.
- Doing three ways merge is impossible. You will need to open the notebook and manually make all the changes.
- Jupyter metadata pollutes the diff history, making it hard distinguish a code update from a metadata change like cells running order.
Small Tool Ecosystem
Tools play an important role in any modern software development environment. However, with Jupyter Notebook they’re either limited, hard to use or non-existent. For example, if you want to use pylint you will need to export your notebook to a python script, then run pylint, after that fix the issues in the notebook and redo. You can’t have it to run on the fly like any IDE from the last 10 years, and this applies to most static code checkers. Also, the intellisense features like autocomplete aren’t usable for real-world projects. As for debugging, it’s usually done with prints or pdb which is primitive, or using an extension that lets you have a visual debugger. However, it isn’t available for all kernels and has very limited features. Example, as of March 2020 the extension doesn’t have conditional breakpoints something introduced decades ago.
Conclusion
For the Jupyter Notebook users out there, I know that it is easier to do all your development using the same tool. However, I think you should aim for having a set of tools appropriate for each task. You’ll thank yourself later. Also, I know that some libraries or tools address or will address the issues that I mentioned in this post. But you need to ask yourself if you want to spend time finding these tools or using something perfected by software developers that work out of the box to bring even more value to your project.
Before You Go
Follow me on Twitter, where I regularly tweet about software development and machine learning.