Turn an Excel Model Into an Application Another Person Can Use

Excel
Python
Applications
Workflow Design
A practical design guide for turning a Python-powered Excel model into a repeatable workbook application that another person can operate without maintaining the Python.
Published

August 27, 2026

A workbook becomes much more useful when another person can operate it without the author sitting beside them. The important transition is not simply moving formulas into Python. It is designing a handoff: durable inputs stay understandable in Excel, multi-step logic has one maintainable implementation, outputs are intentional, and another person can open the same workbook and complete the recurring job.

Boardflare’s Demand & Inventory Planner is a useful example. An inventory analyst maintains the optimization model; a buyer or planner updates demand and visible planning assumptions, then reviews the resulting order plan. The second user does not need to maintain the SciPy model to operate the workbook.

That distinction suggests a better acceptance test for a Python-powered workbook:

Can someone who understands the business task, but does not maintain the Python, use the workbook correctly without the author sitting beside them?

If the answer is no, you still have an analysis. You do not yet have a reliable workbook application.

This post follows that one workbook through seven stages: existing data and assumptions, Python logic, reactive input bindings, a review surface, table selections, App mode, and a save, reopen, and share check. For a wider view of which responsibilities belong in Excel and which in Python, see Migrating Excel Workflows to Python.

Start with the operating job, not the code

Before reorganizing a workbook, write down the recurring job it is supposed to support.

For the inventory planner, the job is not “run a mixed-integer program.” The job is closer to:

  1. update SKU demand and inventory assumptions;
  2. set the available purchasing budget and service assumptions;
  3. generate a feasible replenishment plan;
  4. identify deferred SKUs and solver problems;
  5. review the result and use it in the purchasing process.

That description immediately separates the operator contract from the implementation.

The operator needs visible business inputs, interpretable outputs, status information, and instructions. The author needs the Python model, validation scenarios, diagnostics, and a clear workbook binding contract.

Those two roles can use the same workbook without requiring the same interface.

Stage 1: Keep durable business state in Excel

A useful default is to keep data and assumptions that users need to inspect, edit, save, audit, or reuse in the workbook.

The inventory planner keeps SKU data and its core planning controls in Excel: cash budget, service factor, planning horizon, and case pack. Python reads those values and computes the optimized plan.

That is preferable to hiding important business assumptions inside notebook variables because the workbook remains the durable business artifact. A planner can reopen it and see the state that drove the result.

Typical worksheet-owned state includes:

  • source tables;
  • planning assumptions;
  • thresholds and policy settings;
  • identifiers and labels;
  • inputs consumed by downstream Excel formulas;
  • outputs that need to remain visible in the workbook after review.

The exact boundary varies by workflow. The principle is that saved business state should have an intentional home.

Stage 2: Centralize the software-like logic

Excel is excellent at visible calculations and ad hoc modeling. It becomes harder to maintain when a recurring process needs loops, optimization, matching, simulation, validation, or several dependent transformation stages.

In the inventory example, Python calculates safety stock and replenishment requirements, then solves a mixed-integer optimization problem with scipy.optimize.milp. SciPy’s current documentation describes milp as a solver for linear objectives with bounds, linear constraints, and optional integrality constraints: exactly the kind of logic that becomes awkward when spread across many helper ranges.

The point is not that every Excel formula should become Python. It is that logic which behaves like a program should have a coherent implementation rather than being distributed accidentally through the workbook.

Stage 3: Bind workbook inputs reactively

The notebook reads worksheet state through bf.inputs, which keeps the notebook synchronized with the workbook. Each named binding is a range (or a bf.ref with headers), and downstream cells that use a binding re-run when its values change.

inputs = bf.inputs(
    skus=bf.ref("SKU Inputs!A4:H14", headers=True),
    controls=bf.ref("SKU Inputs!J4:K8", headers=True),
)
inputs

Two details keep the binding maintainable. A notebook can declare at most one bf.inputs() widget, so declare every workbook binding in that one call. And the widget must be displayed as the cell’s last expression so its connection to the workbook stays live. Downstream cells read values by name, as in inputs["skus"].

# in a downstream cell: validate inputs, calculate requirements, solve the model
solution = milp(...)

Stage 4: Give the operator a review surface

The worksheet is not the only place the operator works. The task pane’s Notebook tab hosts the notebook: Edit mode shows the code and authoring controls, while App mode shows the operator-facing widgets, tables, and charts.

A review surface should answer the questions the operator asks before acting on a result. For the planner, that means showing the solver status and budget use next to the order plan, so the operator can see whether the plan is feasible before reading the quantities. Controls the operator changes should sit next to the results they affect.

Keep the Excel output tables as the durable record. The notebook view explains and reviews the result; the workbook keeps it.

Stage 5: Inspect rows with table selections

Sometimes the reviewer needs to act on one row of an existing Excel table. bf.selection reads the row under the active cell and passes it to the same inputs widget. For example, if your workbook has a table named SKUs:

inputs = bf.inputs(
    skus=bf.ref("SKU Inputs!A4:H14", headers=True),
    picked=bf.selection("SKUs"),
)
inputs

Reading inputs["picked"] returns a pandas DataFrame with the table’s headers as columns and at most one row. Look up fields by column name, such as picked["SKU"], because selection rows carry values rather than positions.

The empty case needs design. The result is an empty DataFrame when the cursor is outside the table body, on the header or total row, on another sheet, or on a hidden or filtered row. A filter that hides the active row does not refresh the result until the selection changes. Show an explicit “no row selected” message in the review surface rather than letting the operator read a blank as zero.

Stage 6: Use App mode to focus the operator experience

Once the notebook has become a repeatable internal tool, Boardflare can save it to open in App mode. As of this writing, App mode presents the same notebook with a simplified operator-facing surface: code cells are hidden, and only the UI widgets such as inputs, dropdowns, charts, sliders, and markdown text are displayed. Controls, explanations, tables, and charts can be emphasized over code. An operator who needs to change the code can switch to Edit mode for the current session without altering the saved workbook preference.

It is important to keep the boundary precise: App mode is a presentation preference, not source protection, a permission system, or a separately deployed SaaS application. The saved notebook source remains part of the executable workbook and should be reviewed and trusted accordingly.

The maintained App Mode and Sharing guide documents what is saved, what is rebuilt on open, recipient requirements, and the sharing checklist.

Publish only what the workbook needs

A notebook can contain many intermediate values: cleaned tables, candidate solutions, diagnostic arrays, charts, validation checks, and helper functions. The worksheet does not need all of them.

Treat publication as an interface.

For example, the inventory workbook exposes a compact summary and detailed replenishment table. The notebook can keep solver internals and intermediate calculations in Python unless they are useful to the operator.

publication = bf.publish(outputs={
    "summary": summary,
    "detail": detail,
})
publication

That gives the workbook a cleaner contract:

Excel inputs
    ↓
Python program
    ↓
intentional published outputs/functions
    ↓
Excel review and downstream use

The same rule helps when using BF.FUNCTION(): expose a function because the worksheet needs a reusable calculation, not merely because a Python function happens to exist.

See Working with Excel for the current workbook input and publication model.

Separate durable controls from exploratory controls

Not every control belongs in the worksheet, and not every control belongs in the notebook.

A useful distinction is persistence and business meaning.

Put a control in Excel when it represents a durable assumption or policy that should be visible when the workbook is reopened. Budget, date tolerance, forecast horizon, and an approved confidence threshold are good examples.

Put a control in the notebook when it changes how the operator explores or views results without redefining the underlying saved business case. Filtering an exception table or choosing which diagnostic series to display can be a good fit.

This division also makes the workbook easier to explain. The operator knows which inputs change the business calculation and which controls merely change the view.

Design the error path before sharing

A workbook application is not complete when the happy-path result works. The operator also needs useful behavior when inputs are missing, a package cannot load, a model is infeasible, or an external dependency fails.

For the inventory planner, solver status and budget feasibility are part of the published result. That matters because “no answer” and “optimal answer” are operationally different states.

For other workflows, the application may need to surface:

  • invalid or missing input messages;
  • reconciliation exceptions;
  • forecast validation metrics;
  • unsupported input shapes or type conversion errors;
  • model convergence warnings;
  • data-quality checks;
  • stale or incomplete output states.

Do not make the operator infer these conditions from a blank table or a Python traceback.

Stage 7: The second-user test is the release gate

Before calling a workbook an application, test the actual handoff. Save, close, and reopen the workbook, then check it as the operator will use it.

A practical release checklist is:

  • save the workbook and notebook successfully;
  • close and reopen it;
  • verify the intended Edit/App startup presentation;
  • change representative worksheet inputs and confirm the result recomputes;
  • select a representative table row, and check the empty state with the cursor outside the table;
  • verify every worksheet output and published function used by the workbook;
  • test invalid inputs and expected failure states;
  • verify pre-bundled package imports and startup from a fresh session;
  • verify workbook input bindings and published outputs resolve cleanly;
  • give the workbook to the intended operator and watch whether they can complete the job without author intervention.

That final step exposes problems that unit tests and author testing routinely miss: unclear labels, hidden assumptions, unexplained warnings, output tables that require tribal knowledge, or dependencies that only work on the author’s machine or account.

What the author still owns

Handoff does not mean the Python disappears or maintains itself.

The technical owner still needs to maintain:

  • the workbook input contract;
  • Python business logic;
  • package compatibility;
  • validation cases;
  • published outputs and functions;
  • workbook layout and schema assumptions;
  • documentation when the business process changes.

The goal is narrower and more useful: operation should not require implementation maintenance.

That makes the workbook suitable for recurring work where one person owns the logic and another person runs the process.

A workbook application is a designed handoff

Python can make an Excel model more sophisticated, but sophistication alone does not make it operational.

A reliable workbook application has four deliberate boundaries:

  1. Excel owns durable business inputs and reviewable outputs.
  2. Python owns the software-like logic that benefits from a coherent program.
  3. The notebook exposes an intentional interface rather than every intermediate object.
  4. A second user can operate the saved workbook without maintaining the implementation.

If you want to inspect a working example, open the Demand & Inventory Planner and change the worksheet demand or planning controls. Then use the App Mode and Sharing guide as the checklist for turning the same authoring artifact into a repeatable handoff. For the migration decisions that come before this step, read Migrating Excel Workflows to Python.