Most teams treat function calling as a feature they turn on. It is really a decision about the orchestration layer: who picks the next action, your code or the model. Once you separate the two, the trade-offs become concrete.
In the model-chosen path, you hand the model a list of tool schemas and it returns a tool name plus arguments. The model does not run anything. It pauses, your code executes the function, returns the result, and the model continues. That is an inherently multi-turn, interactive flow, as the structured outputs versus function calling breakdown describes. Function calling relies on instruction tuning: the model is trained to recognize when it lacks information or should take an action.
In the code-routed path, your orchestrator inspects the request and calls a function directly. No model turn is spent on selection. Function calling is a specialization of JSON-schema constrained decoding where the schema names a tool to invoke, according to the ZeroEntropy explainer on constrained decoding. Same machinery, different framing. The point is that both paths produce a name and an argument object; the question is whether a model or a branch statement produced them.
Orchestration framing helps here. The best architecture uses the least flexible control mechanism that can reliably solve each part of the workflow, the orchestration guide at Promptessor argues. Routing can be deterministic, model-driven, rule-based, or hybrid. A single agent plus several tools is often simpler than a fleet, and the same logic applies to selection: do not pay for a model decision where an if statement is both cheaper and more reliable.



