Episode 22 — Understand Libraries and Imports So Automation Doesn’t Break at Runtime

In this episode, we’re going to make sense of a problem that feels mysterious at first but becomes very predictable once you know what to look for: automation that runs fine in one place and then crashes the moment it starts somewhere else. For brand-new learners, this often shows up as a confusing error that points at a missing library, a failed import, or a module that cannot be found. What makes it frustrating is that the automation might have worked yesterday, or it might work on one teammate’s machine but not on yours. Under the hood, this is rarely about your logic being wrong and almost always about the runtime environment not matching your assumptions. By the end, you should be able to explain what an import actually does, why libraries behave differently across environments, and how to think about dependencies in a way that keeps your automation deployable.
A library is simply reusable code packaged in a way that other code can use it, and an import is how your automation asks for that code at runtime. When you import something, you are telling the runtime to locate a specific piece of code, load it, and make its functions or classes available to your script. Beginners sometimes imagine the imported code is copied into the script, but that is not what happens in most cases. Instead, the runtime searches for the requested library in a set of known locations, often called the module search path, and then loads the first match it finds. This means imports are sensitive to where code lives, how it is named, and what environment is running the automation. If the runtime cannot find the library, it fails immediately, and if it finds the wrong version, it might fail later in a more confusing way.
A key idea is that runtime is not the same as development time, even if it feels like one smooth flow when you are learning. Development time is when you are writing and testing on your own system, with whatever tools, cached files, and installed packages happen to be there. Runtime is when the automation executes in the real target environment, such as a scheduled job runner, a container, or a build pipeline. Those environments are often intentionally minimal, and they may not include the libraries you casually installed earlier. This is why an import can work locally and fail in production, because production is not meant to inherit your personal setup. For deployable automation, you have to assume the runtime starts from a clean slate and only has what you deliberately provide.
Imports can fail for simple reasons that are easy to miss when you are new, like name mismatches and shadowing. Shadowing happens when you accidentally give your own file or folder the same name as a library you intended to import, so the runtime loads your file instead of the real library. This can produce weird behavior, because the import technically succeeds, but the code you loaded does not have the functions you expected. Another common issue is relying on relative locations without realizing it, where the import works only if you run the script from a particular directory. In automation, the working directory can be different depending on how the job is launched, so assumptions about where you are standing in the filesystem can break. Clean import behavior means your automation can locate what it needs regardless of who runs it or where it runs from.
Another important distinction is between standard libraries and third-party libraries. A standard library is code that ships with the language runtime itself, so it is typically present anywhere that runtime is installed. A third-party library is something you must install separately, and that is where most deployability problems come from. Beginners often do not realize which libraries are standard and which are third-party, because both are imported in similar ways. The practical impact is that you cannot assume third-party libraries exist in a fresh environment, even if they exist on your laptop. If you forget this, you end up with automation that is silently tied to your personal machine. A good habit is to treat every third-party import as a promise you must fulfill in the deployment process, because the runtime will hold you to that promise.
Version differences are another way imports can lead to runtime failure, even when the library exists. A library might change function names, move modules around, or adjust default behavior between versions, and your code might rely on the old layout. In one environment, you have version A and everything imports correctly, while in another you have version B and the import path is different. This problem can be sneaky because the error might not happen at the import line, but later when you call a function that no longer exists or behaves differently. Deployable automation reduces this risk by being explicit about dependency versions and by testing in an environment that matches production as closely as possible. When you understand imports, you stop treating these failures as random and start seeing them as predictable mismatches between code expectations and library reality.
A practical way to think about import stability is to treat import paths like public addresses. If a library’s maintainers consider something part of their public interface, they try to keep it stable, but internal modules might move around with less warning. Beginners sometimes import from deep internal paths because they found an example online, and it works for a while, until an update breaks it. The safer pattern is to import from the library’s documented entry points, which are more likely to remain compatible. This is less about memorizing rules and more about adopting a mindset of using what the library intends you to use. In automation, you want boring stability, not clever shortcuts. The import line is a commitment, so it should be a commitment to something that is meant to be stable.
It also helps to understand when imports happen and why that timing matters. Many runtimes load imports at startup, meaning your automation may fail immediately before it does any useful work. Sometimes imports happen lazily, meaning the runtime loads a module only when a specific code path is reached. Lazy behavior can hide problems until a particular feature is used, which makes failures look intermittent. For example, a rarely used error-handling path might import a helper library, but you only discover the missing dependency when something goes wrong. In operations, that is the worst time to learn you are missing a library. A deployability mindset tries to ensure that the environment contains everything needed for every code path you might trigger, including recovery and reporting paths, not just the happy path.
Another common runtime trap is assuming the same operating system and architecture everywhere. Some libraries are pure code and run the same on many platforms, while others depend on compiled components or system-level features. Your local machine might be Windows, while production might be Linux, and a dependency might behave differently or fail to install in one of those environments. Even if you are not doing installation steps here, the conceptual issue still matters: an import line might represent code that is not portable. Automation that is meant to be widely deployable should favor dependencies that are consistent across the target platforms. When you cannot avoid platform-specific dependencies, you should at least recognize that your automation is no longer universally portable and needs a standardized runtime environment.
Now connect imports to security in a way that makes sense for beginners. When your automation imports a library, it is effectively trusting that library’s code to run with the same permissions as your automation. If your automation has access to sensitive systems, then imported code can touch those systems too. That is why dependency hygiene matters, and it is why supply chain attacks are a serious concern in modern development. Supply chain attacks are when an attacker compromises a dependency so that anyone who installs or imports it unknowingly runs malicious behavior. You do not need to be paranoid, but you do need to be intentional about where dependencies come from and how updates are managed. Understanding imports helps you see that your automation is not just your code, it is a collection of code you chose to execute.
Libraries and imports also impact runtime performance and reliability, which becomes obvious when automation scales. Importing large libraries can slow startup time, which matters for short-lived tasks that run frequently. Some libraries do work during import, like initializing configuration, scanning the environment, or loading data files, and that can produce delays or failures before your logic even starts. This is one reason experienced engineers pay attention to what they import and when. For automation, startup reliability is part of deployability, because if the job is scheduled to run every few minutes, a slow or flaky startup can cause backlogs and cascading failures. The goal is not to avoid useful libraries, but to choose dependencies that behave predictably and to structure code so imports do not create hidden surprises at runtime.
A helpful mental model is to picture the runtime environment as a sealed backpack you hand to the automation right before it runs. Everything the automation needs must already be inside that backpack: the correct runtime, the correct libraries, and the correct versions. If your automation only runs when it can reach outside the backpack to grab something from your laptop or from a previously cached state, then it is not truly deployable. Imports are the moment your code reaches into that backpack and tries to pull out the tools it needs. If the tool is missing, you fail immediately, and if the tool is wrong, you may fail later in more confusing ways. This model keeps you honest because it forces you to think about what the environment contains, not what you wish it contained.
Because automation often involves external services, you will also encounter software development kits, and it helps to name that concept carefully. A Software Development Kit (S D K) is a collection of libraries and helpers designed to make interacting with a service easier, usually by wrapping network calls and providing convenient functions. If your automation relies on an S D K, then import failures can stop your automation from even connecting to the service. Even when the import works, version differences can change how requests are formed or how errors are handled, which can change behavior in subtle ways. This is where beginners often confuse application logic errors with environment errors. If the automation cannot import the library, that is an environment problem, but if the library imports and then behaves unexpectedly, that might be a compatibility or version problem. Knowing the difference helps you troubleshoot faster and prevents you from chasing the wrong cause.
A similar concept shows up when your automation interacts with an Application Programming Interface (A P I). An A P I is the defined way one system talks to another, and libraries often exist to make using an A P I easier and safer. When you import a library that wraps an A P I, you are trusting that wrapper to match the service’s expectations. If the wrapper is outdated, it may call endpoints that no longer exist or assume fields that have changed. This can look like the service is broken, but the root cause might be your library version, not the service itself. Deployable automation plans for this by keeping dependencies current in a controlled way and by understanding that imports are the beginning of a chain of assumptions. When you think in terms of assumptions, you become better at designing automation that stays stable even as the world changes.
To tie everything together, the main reason libraries and imports cause runtime breakage is that they are where your code meets the environment. Your logic might be correct, but if the runtime cannot locate the library, cannot load it, or loads a different version than expected, the automation becomes unreliable. For deployable automation, you want predictable imports, minimal dependency surprises, and a runtime environment that is defined, reproducible, and consistent across systems. When you understand imports, you stop blaming yourself for every crash and start asking the right questions about what the runtime can actually see and load. That shift is empowering because it turns mystery failures into checkable conditions. If you build your automation with that mindset, you will ship workflows that behave the same way in development, testing, and production, which is exactly what deployability is supposed to mean.

Episode 22 — Understand Libraries and Imports So Automation Doesn’t Break at Runtime
Broadcast by