Odoo Studio is a genuinely impressive piece of engineering. Point-and-click field creation, drag-and-drop form layouts, automated actions built without writing a line of code. For a business owner who wants to add a field to a quote before the quarterly review, it delivers. Fast, no developer needed, done.
We don't use it for production client work. The reason isn't that it's a bad tool. The reason is that it's the wrong tool for systems that have to survive version upgrades, multiple developers, and five years of evolving requirements. Understanding exactly why requires knowing what Studio actually does under the hood, and what it leaves behind when the customization gets complicated enough to need real code.
What Studio actually produces
When you use Studio to add a field, modify a view, or create an automated action, Odoo does not write Python or XML files to disk. It writes records into the database. Those records live in the same PostgreSQL tables as your business data: ir.model.fields for fields, ir.ui.view for view modifications, ir.actions.server for automated actions, and so on.
Any field you create through Studio gets the x_ prefix automatically. That is the system's way of marking it as data-driven rather than code-driven. x_credit_tier, x_approval_notes, x_dispatch_flag. The prefix is honest labeling, and it is the first sign that Studio customizations occupy a different category from module code.
The practical consequence: your Studio customizations are not in your source tree. They are not in version control. They cannot be code-reviewed. They cannot be diffed. They exist as database records, exported as a .studio bundle if you explicitly package them, but they do not live anywhere that a developer can open, read, branch, test, and deploy with the same discipline as the rest of the codebase.
For a one-off tweak, that is fine. For the operational backbone of a wholesale distribution business, it is a problem that compounds over time.
The version upgrade problem
Odoo releases a new version annually. Enterprise clients on a maintained version eventually upgrade. That upgrade is a substantial project under any circumstances. Studio customizations make it harder in a specific and predictable way.
Module code upgrades with a structured process. You update dependencies, run the upgrade scripts, resolve the breakage in your Python and XML, test, and ship. The failure modes are visible: a stack trace, a missing field reference, an XPath that no longer matches the restructured view. They are diagnosable because the code is right there.
Module code also carries its history in comments. When we write Python, we document the what, the how, and the why: why this field exists, what business rule it encodes, why the logic is structured the way it is. That commentary is worth its weight during an upgrade. When you're looking at a method six versions later and trying to decide whether the behavior still applies, a comment that says "this constraint exists because the warehouse team manually reconciles transfers on Fridays and the standard Odoo flow closes the picking before they finish" tells you exactly what to verify. Studio has no equivalent. There is no place to record intent. The customization exists as a record in the database, and the reasoning behind it exists only in someone's memory, or not at all.
Studio customizations do not carry forward cleanly either. The database records that encode your Studio work were written against the data model of the version you were on. The new version may have changed the underlying views your Studio modifications were attached to. It may have changed field behaviors, form layouts, or model structures in ways that make the stored customizations invalid, silently broken, or simply gone.
The upgrade path for Studio work is to re-export, re-import, and manually verify every customization in the new version. There is no diff. There is no automated regression test. There is a person clicking through forms hoping nothing was dropped. On a system with dozens of Studio modifications accumulated over three years, that verification process is not a morning's work.
Custom module code has its own upgrade burden, and we're not pretending otherwise. But the burden is tractable. The code is enumerable. The test surface is defined. The comments tell you why each decision was made. Studio's upgrade burden is proportional to how much was done, how long ago, and how thoroughly it was documented, which is often not at all.
The version-control and handoff problem
Any serious development practice runs on version control. Every change is committed, every commit is reviewable, every deployment is reproducible from source. This is not a preference. It is the minimum infrastructure for maintaining a system over time without losing your mind.
Studio sits entirely outside this. You cannot git blame an x_ field to find out who added it and why. You cannot check out last month's state of your Studio customizations. You cannot run a staging environment that is provably identical to production at the code level. The Studio bundle export gives you a snapshot, but it is a point-in-time artifact, not a living source tree.
The handoff problem follows directly. When a new developer, a new consultant, or a new IT director inherits a Studio-heavy system, there is no codebase to read. There are database records. Understanding what was customized, why, and how the pieces fit together requires either the original person who built it or a significant amount of archaeology through the Studio interface. Neither is a good foundation for continued development.
We do project rescue work. A meaningful share of the systems we inherit have Studio customizations that the client cannot fully explain and the previous consultant is no longer available to clarify. That is not an argument against the tool. It is an argument for knowing its limits before you build a production system on it.
The depth limit
Studio has a hard ceiling on what it can express. You can add fields. You can modify views. You can create simple automated actions. You cannot write business logic that requires looping over related records, calling external APIs, enforcing constraints across multiple models, or implementing anything that requires actual Python.
Most production systems eventually need something past that ceiling. The question is what you have when you get there.
If the system was built in modules from the start, the developer inherits a structured codebase with consistent naming, a known module layout, version-controlled history, and a scaffold to extend. Adding a complex workflow to a well-structured codebase is a scoped problem.
If the system was built primarily in Studio, the developer inherits a gap. The Studio work is in the database. The new code needs to go into a module. Those two things need to interact: the module code has to reference x_ fields by name, account for the Studio-defined view inheritance, and work around the data-driven customizations rather than alongside module-driven ones. The result is two architectural layers that don't share conventions, naming standards, or a common home. Maintaining that hybrid is harder than maintaining either one alone.
We've scoped projects where the client expected a modest extension of their existing Studio-built system, and the right answer turned out to be rebuilding the Studio layer as proper module code before adding anything new. That is not the answer anyone wanted. It's the honest one.
Where Studio is the right call
There are real use cases where Studio is the correct tool and custom module development would be overkill.
- Prototyping and discovery. Studio is excellent for showing a client what a workflow could look like before committing to building it in code. We sometimes use it exactly this way in early project phases: build a rough version in Studio, walk through it with the client, learn what they actually need, then build the real thing in a module. The Studio version gets thrown away. That's the right outcome.
- One-off fields with no lifecycle pressure. A single informational field on a model, used by a handful of people, unlikely to be touched again. If the business accepts that this field may need manual attention on the next version upgrade, Studio is fine.
- Small deployments with limited customization scope. A service business using Odoo primarily for CRM and invoicing, with light customization needs and no plans for deep workflow automation. The upgrade burden stays manageable if the Studio footprint stays small.
- Internal tools with a short expected lifespan. A temporary tracking view for a project that ends in six months. Nobody needs that in a module.
The common thread: Studio is appropriate when the expected lifespan is short, the scope is narrow, or the use is deliberately throwaway. When any of those conditions doesn't hold, the calculus shifts.
The production criterion
The question we ask before deciding how to build something: will this be here in five years, and will it need to survive at least two version upgrades?
If the answer is yes, it gets built as a module. Named with our ds_ conventions, version-controlled from day one, with a manifest, a security file, proper view inheritance in XML, and Python that can be read, tested, and maintained by a developer who wasn't there when it was written. That Python carries comments explaining why each decision was made, not just what the code does. Those comments are what make the difference between an upgrade that takes a week and one that takes a month.
If the answer is no, Studio may be exactly right.
The mistake is not using Studio. The mistake is using Studio without asking that question, then being surprised three years later when the answer turns out to have been yes all along.
If you're running an Odoo deployment that was built primarily in Studio and you're starting to feel the friction, the first honest step is understanding what you actually have. How many Studio customizations exist, which ones carry real business logic, and what a migration to module code would realistically cost. DimeSoft has done this assessment enough times to give you a calibrated answer, not a worst-case estimate. If that conversation would help, reach out and we'll start there.