Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I avoid a function with too many unrelated responsibilities?

Quick answer

List what the function does, then group the steps by purpose: for example, preparing input, applying a rule, and presenting a result. Extract a group when it has a clear name and boundary, or when separating it makes the main flow easier to understand or change. Keep tightly connected steps together. A helper is not automatically better just because it is possible to create one.

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

What are the signs that a small function is doing too many unrelated things? I don’t want to split it just because it has a few lines.

Geez

Look for steps that answer different questions or would change for different reasons. A function that reads a value, checks whether it is allowed, calculates a result, and formats a message may actually contain several jobs. A few lines that all contribute to one clear calculation, on the other hand, may belong together.

Try writing a short verb phrase for each group of steps. If the phrases are different—“normalize input,” “apply the rule,” “format the message”—and each group has a clear boundary, consider a helper. Then check whether the original function’s main flow becomes easier to scan. Splitting that makes readers jump around without improving the names or structure is probably not helping.

Follow-up question

Could you show that with a before-and-after example? Assume a function prepares a report from a list of scores.

Geez

Assume this is an ordinary in-memory example and that the exact rules are placeholders. The point is how the work is organized, not what score rules a real program should use.

Before, one function might do all of this in sequence:

buildReport(scores):
    remove entries that should not be included
    calculate the summary from the remaining entries
    format the summary as report text
    return the report text

The name buildReport gives the overall goal, but each step has a different purpose. A clearer arrangement could be:

buildReport(scores):
    included = selectIncludedScores(scores)
    summary = calculateSummary(included)
    return formatReport(summary)

Now each helper name describes one part of the flow, and buildReport reads like a short outline. These names are illustrative; they do not prescribe particular filtering or calculation rules. If a helper would be just one opaque line, or its name would merely repeat the code, keep that step inline instead.

Follow-up question

How do I choose where the helpers should live, and avoid creating too many?

Geez

First ask whether the helper has a useful name and whether it belongs to this one function or has a wider use. In C#, a local function is a named helper declared inside a member and visible only there. Microsoft recommends that option when descriptive helper logic is needed by one member; use a regular method when multiple members need it, and a lambda when behavior is short and clear at its use site. It also advises keeping captured values few and obvious, or passing needed values explicitly to a static local function. These are C#-specific organization choices, not a universal test for finding unrelated responsibilities. [S1]

For any language, use the smallest boundary that makes the intent clearer. Don’t extract a helper just to reduce the line count. If it adds indirection, hides important details, or needs an awkward pile of parameters, reconsider the split. A helper can also be worthwhile when you want to name and reason about a distinct step, even if it is not reused.

Follow-up question

What quick review can I use before I commit the refactor?

Geez

Review the result from the caller’s point of view: can you understand the sequence without reading every implementation detail? Then inspect each helper in turn: does its name describe one job, are its inputs and output understandable, and did the split leave related logic awkwardly separated? Finally, compare the old and new behavior using the checks already available for your project. The example above doesn’t establish any particular score rule or guarantee that a refactor preserves behavior; verify that against your actual requirements.

A practical checklist

  • Write a short verb phrase for each distinct group of steps.

  • Keep steps together when they serve one purpose and read clearly as a unit.

  • Extract a group only when its name and boundary make the main flow easier to understand.

  • Choose a local helper for one-caller logic or a wider-scope method when multiple callers need it; apply this distinction in C# as described by Microsoft. [S1]

  • Review helper inputs and outputs, then check behavior against the project’s existing requirements.

A few more useful answers

Should every helper be reusable?

No. A helper can clarify one function without being reused. Its name and boundary should make the code easier to follow.

Is a long function always a sign of unrelated responsibilities?

No. Length alone does not show whether the steps belong together. Consider their purpose, how clearly they read as a unit, and whether separating them improves the main flow.

Does the C# local-function guidance apply to every programming language?

No. The cited guidance describes C# options specifically. Other languages may offer different ways to keep a helper local or make it available more widely.

Sources

References used for this guide.

  1. [S1] Local functions — C#
    learn.microsoft.com · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work