38 Files, 0 Patience

A Claude Code Story

Who am I?

Android Signal

What is Claude?

Claude Anthropic

I Built a DSL to Avoid Tedium


configure {
  switchPref(
    title = DSLSettingsText.from(R.string.title),
    isChecked = state.enabled,
    onClick = { viewModel.toggle() }
  )

  dividerPref()

  clickPref(
    title = DSLSettingsText.from(R.string.setting),
    onClick = { navigate() }
  )
}
          

Then Compose Happened


@Discouraged("The DSL API can be completely replaced
              by compose. See ComposeFragment or
              ComposeBottomSheetFragment for an
              alternative to this API")
fun configure(init: DSLConfiguration.() -> Unit)
          

The Old Way


class MySettingsFragment : DSLSettingsFragment() {
  private val viewModel: MyViewModel by viewModels()

  override fun bindAdapter(adapter: MappingAdapter) {
    viewModel.state.observe(viewLifecycleOwner) { state ->
      adapter.submitList(getConfiguration(state).toMappingModelList())
    }
  }

  private fun getConfiguration(state: State): DSLConfiguration {
    return configure {
      sectionHeaderPref(R.string.header)
      // switchPref, dividerPref, clickPref, etc.
    }
  }
}
          

The New Way


class MySettingsFragment : ComposeFragment() {
  private val viewModel: MyViewModel by viewModels()

  @Composable
  override fun FragmentContent() {
    val state by viewModel.state.observeAsState()
    val callbacks = remember { DefaultMySettingsScreenCallback(...) }

    MySettingsScreen(state = state, callbacks = callbacks)
  }
}
          

For Each of 38 Files...


MySettingsScreen           // Screen composable
MySettingsScreenPreview    // @Preview function
MySettingsScreenCallback   // Callback interface
DefaultMySettingsScreenCallback // Default impl
          

Sequential Prompts

Human architects + Human orchestrates


Me → Claude: "Generate DefaultCallback"
Me → Claude: "Generate Callback interface"
Me → Claude: "Create Screen from getConfiguration"
Me → Claude: "Swap base class, wire it up"

                    ×38 files
          

Deliverables List

Human architects → Claude orchestrates


Migrate PrivacySettingsFragment to ComposeFragment.

Key mappings:
- clickPref → Rows.TextRow
- switchPref → Rows.ToggleRow

Create:
1. PrivacySettingsScreenCallback interface
2. DefaultPrivacySettingsScreenCallback
3. PrivacySettingsScreen composable
4. PrivacySettingsScreenPreview
5. Update fragment to extend ComposeFragment
          

History as the Prompt

Claude architects + Claude orchestrates


Migrate PrivacySettingsFragment to ComposeFragment.

Look at commit 683da1f167 ("Convert expire timer
settings fragment to compose") for the pattern.
          

The Role Evolution

Architect Orchestrator
Sequential Human Human
Deliverables Human Claude
History Claude Claude

What Worked Well


clickPref      →  Rows.TextRow      ✓
switchPref     →  Rows.ToggleRow    ✓
dividerPref    →  Dividers.Default  ✓
customPref     →  ???
          

The Tricky Bits


onClick = { showDialog() }  // Not just a mapping
onClick = { viewModel.doThing() }  // Needs callback wiring
          

Iterate, Iterate, Iterate


/plan → plan.md → refine → execute → repeat
          

Your Intuition Will Be Wrong

(And That's OK)

Questions?

This presentation was built with:

reveal.js reveal.js
Claude