Should I choose simple interfaces or small functions? Clean Code vs deep modules debate

I’ve been thinking about two different approaches to writing code and I’m not sure which one to follow.

One approach says we should create modules that hide complexity behind simple interfaces. The user doesn’t need to know much to use them. This comes from John Ousterhout’s book on software design philosophy.

The other approach from Robert Martin’s Clean Code says we should break everything into small focused functions. This usually means more interfaces to work with.

I always liked the small functions approach because it worked well for me. But recently I had trouble with a component library that had too many options. Here’s what I mean:

Complex component with many parts:

import { MenuSystem } from "ui-library";

function MyMenu() {
  return (
    <MenuSystem.Container>
      <MenuSystem.Button />
      
      <MenuSystem.Overlay>
        <MenuSystem.Panel>
          <MenuSystem.Title />
          <MenuSystem.Option />
          
          <MenuSystem.Section>
            <MenuSystem.Option />
          </MenuSystem.Section>
          
          <MenuSystem.Checkbox>
            <MenuSystem.Check />
          </MenuSystem.Checkbox>
          
          <MenuSystem.Divider />
        </MenuSystem.Panel>
      </MenuSystem.Overlay>
    </MenuSystem.Container>
  );
}

Simple component that hides complexity:

<SimpleMenu
  title="User Actions"
  items={[
    { url: '/profile', text: "Edit Profile" },
    { url: '/settings', text: "Settings" },
    { url: '/logout', text: "Sign Out" },
  ]}
/>

The first one gives you more control but it’s harder to learn and use. The second one is much easier but less flexible.

How do you decide when to make things simple vs when to break them into smaller pieces? I’m looking for practical advice on this trade-off.

I hit this same problem last year while refactoring a data processing pipeline. Timing matters way more than picking the “right” approach upfront. When I’m prototyping, I stick with simple interfaces - they let me test ideas fast without drowning in implementation details. Once I know what I actually need and performance starts mattering, I add the granular controls that power users want. Here’s what I learned: breaking things down too early screws you over more than keeping them together too long. Split too soon and you’re guessing how the pieces should fit - those guesses are usually wrong. A cohesive interface forces you to actually understand the problem before you commit to abstractions.

What if you start simple and expose the complex stuff later? Like SimpleMenu handles 80% of cases, but you also export the granular pieces for customization? Have you tried this approach or seen libraries nail it?

depends on your audience. if it’s just you or your team, go flexible - you already kno the codebase. but if you’re building for other devs or open source? keep it simple. I’ve watch tons of libraries fail cuz they were too complex to pick up.