Software Design Approaches: Simple Interfaces vs Small Function Breakdown

I’ve been thinking about two different ways to write code after reading some books and running into issues on a recent project.

One approach says to build modules that hide complexity behind simple interfaces. The other approach (like in Robert Martin’s work) says to break everything into tiny, focused functions.

I ran into this problem with a component library recently. Their MenuDropdown was super flexible but really hard to use:

import { MenuDropdown } from "ui-lib";

export const MyMenu = () => (
  <MenuDropdown.Container>
    <MenuDropdown.Button />
    <MenuDropdown.Overlay>
      <MenuDropdown.Header />
      <MenuDropdown.Option />
      <MenuDropdown.Section>
        <MenuDropdown.Option />
      </MenuDropdown.Section>
      <MenuDropdown.Toggle>
        <MenuDropdown.CheckIcon />
      </MenuDropdown.Toggle>
      <MenuDropdown.Divider />
      <MenuDropdown.Pointer />
    </MenuDropdown.Overlay>
  </MenuDropdown.Container>
);

Compare that to something simpler:

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

The first one gives you tons of control but requires learning so many parts. The second one is way easier to use for basic cases.

How do you decide when to favor simple interfaces that hide complexity versus breaking things down into smaller pieces? I keep going back and forth on this.

depends who’s using it. building for ur team? keep it simple. open source lib that’ll get used everywhere? go granular - it’s worth the learning curve. i’ve watched too many “simple” components turn into monsters when devs jam every feature request into props.

This tension between flexibility and simplicity has shaped how I build things. I start with the simple interface and only add granular components when I actually need them. I learned this the hard way during a legacy codebase refactor - premature flexibility usually creates more problems than it solves. Your SimpleMenu example probably handles 80% of real scenarios without making people think too hard. When teams hit that remaining 20% that needs customization, that’s your signal to either add more props to the simple API or give them lower-level building blocks as an advanced option. The best libraries I’ve used do both - high-level components for common stuff and primitives for edge cases. You avoid choice paralysis upfront but still have an escape hatch when things get weird.

that’s a reely interesting dilemma! i’m curious - what happens when your team grows? do new devs struggle more with the complex flexible approach, or do they prefer having granular pieces to work with? also, have you noticed patterns with maintenence - which style makes bugs easier to track down?