UDTs are cleaner when library boundaries stay boring
Pine user-defined types and libraries can make large scripts clearer, but cross-library object design needs restraint.
User-defined types make Pine feel more like a structured programming language. A script can group related fields, attach methods, and pass richer objects through functions. Libraries make that structure reusable. Together, they are powerful enough to create a different problem: overdesigned boundaries.
I started keeping a note on this because UDT discussions often move quickly from “this would clean up the script” to “let’s build a universal object model.” Pine can benefit from structure, but it still rewards simple dependencies.
Why this catches people
Where people usually go wrong is making a library depend on too many other libraries because the object model feels elegant. A UDT that is perfect for one library may not be the right public type for another. Once libraries depend on each other’s types, versioning and reuse become more fragile.
The opposite mistake is refusing UDTs entirely and passing long lists of loosely related values through every function. That keeps dependencies low but makes the code harder to read and easier to misuse.
The Pine bit
I prefer UDTs that match a library’s responsibility. A drawing library can define a drawing state. A signal library can define a signal state. A backtest-helper library can define a trade summary. Those types do not have to become one universal data object.
Methods are most useful when they make the type’s lifecycle obvious: initialize, update, render, reset, invalidate. If a method silently reaches across too many concerns, the type is probably doing too much.
type Zone
float top
float bottom
int bornAt
bool active
How I handle it in builds
In custom scripts, I use UDTs when they reduce mistakes. Zones, dashboard rows, alert events, and drawing bundles are good candidates. I avoid them when a simple pair of variables would be clearer. Pine users who inherit the script should not need to understand a private framework just to move a label.
For libraries, I keep public types small and stable. If the type may change often, I am cautious about exporting it widely.
Where this shows up
The field test for a UDT is whether it makes the calling code easier to read. If a function call with a Zone object is clearer than passing top, bottom, age, line ID, box ID, and active flag separately, the type is earning its place. If the reader has to open three libraries to understand the object, the structure may be too clever.
I keep serialization in mind. Pine objects are not external database records. They are script structures that live inside the chart execution model. A UDT should help the script calculate, render, or organize state. It should not pretend to be a complete application architecture unless the project genuinely needs that level of complexity.
A good UDT also makes invalid state harder to represent. If a zone must have a top, bottom, birth bar, and active flag, grouping those values reduces the chance that one function receives a top from one zone and a bottom from another. That is not abstraction for its own sake. It is error prevention.
Library boundaries are where restraint matters. Export the type when outside scripts truly need to create or inspect it. Keep it internal when it only exists to organize one library’s implementation. That choice keeps future changes from becoming breaking changes across unrelated tools.
How I test it
I test library structure by reading the public surface first. If the exported types and functions are understandable without opening every internal helper, the boundary is probably healthy. If the public surface leaks implementation details, the library will be harder to reuse.
Checks before I trust it
- Let each library own types that match its actual responsibility.
- Avoid universal UDTs that drag unrelated dependencies across scripts.
- Use methods for lifecycle actions, not hidden cross-system side effects.
- Keep exported types small and stable.
- Choose UDTs when they reduce real misuse, not just because they feel elegant.
The practical takeaway is that Pine structure works best when the boundaries are modest. A good UDT clarifies a script. A grand object model can make a small chart tool harder to maintain than it needs to be.