It depends on which intended domain of usage you are thinking about.
For "reproducibility" or "experimentation" in a production-like R&D environment or university lab, there is one set of downsides. For pedagogy there is another.
From a reproducibility point of view, notebooks are bad because you'll always need the underlying software you're writing to have good modularity anyway. One-off helper functions or any pieces of important business logic, etc., need to be factored out into separate packages or libraries that can be imported anywhere they are needed. "Hard coding" implementations into a notebook is very bad for this, because if you don't exercise an unrealistic degree of care, then certain cells or definitions will rely implicitly on values or imports from other cells, and the interdependencies are a mess to untangle if you are trying port the code from the notebook into a more modular form. Experience has shown me that you don't gain any boost in productivity or speed for experimentation or tinkering if you ignore this and try to make things more modular later on. You just end up writing really sloppy notebooks that have to function as standalone scripts, and you waste some other engineer's time who has to go back and undo the mess.
If you just start out developing in source files in the natural and idiomatic package / library approach of your underlying language, it gets you 90% of the way there for zero extra effort.
The other big thing is that regardless of whether you are writing experimental code to test an idea or a prototype, or you are writing production code, you should be using code review in both cases. Particularly in the experimental case, as in machine learning or statistics, many of the mistakes that make you go back and waste a lot of time are because of methodological errors or diagnostic errors that should have been reviewed by other researchers before you execute the experiment or prototype -- which means you want your code in a good format for simple code review tools, like PRs in GitHub or analyzing diffs. Notebook formats are notoriously bad for this, and since you can accomplish any of the same dev tasks without a notebook anyway, it's an argument against trying to shoehorn a code review process to use some extra type of tooling for analyzing code review artifacts directly from a notebook file.
Along the same lines, notebooks end up encouraging you to have a large block of imports that implicitly define your dependencies, including resources, settings, environment variables, local files, URL settings, etc. etc. This stuff should always be factored out into a maintainable settings artifact of some type that can also be version-controlled and reviewed.
So once you refactor any kind of reusable logical components, refactor any hard-coded settings, use proper dependency management, and put anything of any importance into source files that facilitate easier packaging / sharing / code review ... all you're left with in the notebook is shallow plotting code, which can just as easily be in a source file as well.
Once at that point, I don't think it matters if it's a notebook or not, and it would be fine to use notebooks as extremely shallow presentation tools that do nothing but import other source packages and produce plots / tables / etc. But again, if you've gone that far, there are better ways to produce the same plotting artifacts, etc., and to get away from reliance on the browser as the display medium, and to improve code review even of the plotting artifacts.
For pedagogy I think the problems are different. First you have to think about what is the goal? If the goal is to teach programming concepts, then you are better off teaching them in the idiomatic execution paradigm of whatever language you're using. So distribute source files and instructions to students, rather than isolated notebook cells. Consuming this stuff by looking at it through a web browser is not a relevant property in this case, because the lesson has nothing to do with how the source code is visualized. You're essentially just teaching the students to be impractically lazy by relying on notebook cell execution as the means to advance in the lesson, when they should be learning the appropriate idiomatic execution mechanism of the language they are learning.
A notebook could be useful for distributing a browser-based visualization of a concept if your goal is just to make a web app or something. Like, maybe you want to show an animation of the heat equation to teach a differential equation lesson, or plot a path of a projectile for physics. What's unique about these cases is that the lesson you're trying to teach is specifically unrelated to any source code, or even any particular language. In that case, it would be better to just make an actual web application using front-end tools. I admit substituting Jupyter is probably fine for this, because it also passes my earlier claimed test: students can treat these types of notebooks as throwaway gists... the source code is wholly irrelevant, only the take away concepts they are visualizing or poking around with using some type of visualization widget.
I think the big lie about notebooks is that these two cases (teaching a software concept and teaching a non-software concept that needs interactive visualization) should for some reason go together.
But 99% of the time they would never make sense together, and it makes the notebook suffer from a problem like PowerPoint being all things to all people. The 1% of the time when it might make sense is if you are actually visualizing a computer science concept itself, like visualizing the steps of quicksort and you'd like the actual source code to be implemented right next to the visualization. But even for this, the visualization of the concept should rarely be tied to a single language implementation, and studying the language-specific implementation should rarely involve visualization.