Best Practices
After nineteen chapters of mechanisms, the discipline that separates a maintainable Kivy app from a tangle is a set of rules about where things live and how they connect: the KV owns the layout, the Python owns the behavior, the properties are the reactive seam, the data is the source of truth, and the UI thread is sacred. This chapter collects the rules, each tied to the chapter that introduced the mechanism it governs - because the practices are not new ideas, they are the decision criteria for the tools you now have.
Rule 1: KV for Layout, Python for Behavior
The division from the first-app chapter is the load-bearing structure: the .kv file declares the widget tree, the static property values, and the standard event wiring (on_press: root.method()), and the Python owns the logic - the methods, the data, the properties that drive the UI. The violation is behavior leaking into the KV (a long on_press expression that is really a function) or layout leaking into the Python (building a screen's widget tree in __init__ when it should be a rule). The test is the reader's: can the layout be read in one file and the behavior in the other, without cross-referencing? When a KV expression grows past a single method call, move it to a Python method; when a Python __init__ is adding more than a couple of widgets, move the tree to a KV rule. The custom-widget chapter is where the two meet deliberately - a component's children in its rule or its __init__, its properties in the class - and the component is the unit that keeps the separation honest at the part level.
Rule 2: Properties Are the Seam, One Direction
The reactive layer is the properties chapter's territory, and its rules are the app's stability: declare properties (not plain attributes) for anything that drives the UI, bind source-to-target in one direction (never a cycle), use the right tool per shape (the KV << for display mirrors, bind for cross-object reactions, on_<prop>_change for a class's own side effect, AliasProperty for a derived value), and unbind on teardown. The data-driven and screens chapters apply the same rule to navigation and lists - the data is set before the screen appears, the list is a function of the data - and the canvas chapter makes the drawing a pure function of its properties. The violation that breaks apps is the two-way binding (A drives B, B drives A) and the plain-attribute state (a change that notifies nothing, so the UI goes stale). Keep the properties typed and the bindings one-way, and the "why did the UI not update" class of bug largely disappears, because the update is the mechanism, not a manual refresh someone forgot.
Rule 3: Data Is the Source of Truth, the UI Is a View
The data-driven chapter's rule is the app's architecture: a store owns the data (the file, the network, the database), and the screens and lists are views over it - a data change is a UI change through a refresh, and the UI never holds the truth as its own. The navigation chapter's "populate before switching" and the screens chapter's "the screen is a view over shared state" are the same rule at the page level, and the storage chapter's durable store (load tolerant, write atomic) is the rule at the persistence level. The violation is the screen that keeps its own copy of the data (stale after a refresh elsewhere) or the store that the UI mutates directly without the save (lost on restart). The test: after any change, is there exactly one place the truth lives, and is every screen a projection of it? When the answer is yes, the app is maintainable at any size, because the data flow is one direction - store to view - and the threading chapter's pattern (the result lands in the store, the store triggers the view) keeps even the async cases in the same shape.
Rule 4: The UI Thread Is Sacred
The threading chapter's rule governs all concurrency: the event loop renders and reacts on one thread, long work runs on a thread, and the Clock is the only door back. The networking and media chapters are applications - the request on a thread with the result on the Clock, the playback state as properties the loop renders. The violations are the freeze (a blocking call in a handler) and the race (a worker touching a widget directly). The practices that keep it clean: a _busy guard against stacking work, a timeout on every request, the threadsafe decorator for the repetitive handoff, and the lock-on-the-data (the Clock serializes the UI, not your data structures). The rule is absolute because the consequence is total - a blocked UI thread is a frozen app, and a raced widget is a crash or a silent wrong state - so the "is this on the UI thread?" question comes before every line of code that touches a widget or a property.
Rule 5: Components, Not Screens Full of Widgets
The custom-widgets chapter is the maintainability tool: a screen is a composition of named components (StatRow, RecipeItem, Avatar), each with a small property surface and its own bindings, rather than a screen that builds its widgets inline. The Garden and ecosystem chapters extend it - a garden package is a community component, KivyMD is a themed component set - and the data-driven list is the component pattern at the collection level (an item widget rendered from data). The violation is the screen that is a pile of inline widgets with the logic scattered through the KV expressions, where a change to one row means editing every row. The test: can a part of the UI be reused, tested, and changed in one place? When the answer is yes - because it is a component - the app scales; when it is no - because the part is inline - the app's maintenance cost grows with its size. The rule is the composition-over-inheritance principle from the OOP tutorial applied to the UI tree: the screen has components, it does not re-derive them.
The Review Checklist
- Is the layout in the KV and the behavior in the Python? No long KV expressions, no inline widget trees where a rule belongs.
- Are the state-driving values declared properties, with one-directional bindings and the unbind on teardown?
- Is there one source of truth? The store owns the data; the screens and lists are views; a change flows store -> view, never the reverse.
- Is every widget access on the UI thread? Long work on a thread, the result on the
Clock, a timeout on every request, the busy guard in place. - Is the UI a composition of components? Named parts with small property surfaces, reusable and changeable in one place - not inline piles of widgets.
- Are the assets resolved at runtime and the build config listing them? The packaging chapter's rule, checked before every release.
Summary
The practices are the decision criteria for the mechanisms: the KV owns the layout and the Python owns the behavior (the first-app separation, kept honest at the component level); the properties are the reactive seam, declared and bound one-way with the right tool per shape (the properties chapter); the data is the source of truth and the UI is a view (the data-driven, screens, and storage chapters); the UI thread is sacred - long work out, the Clock back, the timeout and the busy guard in place (the threading chapter); and the UI is a composition of named components, not inline piles of widgets (the custom-widgets and ecosystem chapters). The review checklist is the six questions that catch the violations: the separation, the one-way properties, the single source of truth, the UI-thread discipline, the component composition, and the runtime asset paths. Mastered together, they are what turn the nineteen chapters of mechanism into an app that stays responsive, stays in sync, and stays maintainable as it grows - the Kivy model working as a design system instead of a box of parts.