Step 1 — Define the state
dp[i] means "the answer for the subproblem ending at / of size i". Define it in plain English before writing a single line of code. Ambiguous state definitions cause wrong recurrences. For 2D problems: dp[i][j] = "the answer considering the first i items of list A and first j items of list B".
Step 2 — Write the recurrence
How does dp[i] relate to smaller subproblems? This is the heart of DP. For Coin Change: dp[i] = min(dp[i - coin] + 1) for all valid coins. For LCS: dp[i][j] = dp[i-1][j-1] + 1 if match, else max(dp[i-1][j], dp[i][j-1]). If you cannot express dp[i] using only earlier values, your state definition is wrong — go back to Step 1.
Step 3 — Set the base cases
Base cases are the stopping conditions. They are usually the smallest valid inputs: dp[0] = 0, dp[1] = 1, or dp[i][0] = i for all i. Missing base cases cause index-out-of-bounds bugs or incorrect results that are hard to trace.
Step 4 — Optimize space if possible
Many 1D DP arrays can be reduced to two variables (curr and prev). Many 2D arrays can be reduced to a single rolling row. Only do this after the correct solution is working — premature space optimization is the source of many DP bugs in interviews.