Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Move fast, don't break your API
Search
Amber Feng
September 30, 2014
Programming
18
53k
Move fast, don't break your API
Amber Feng
September 30, 2014
Tweet
Share
More Decks by Amber Feng
See All by Amber Feng
Pixels & Tactics
amfeng
1
9.7k
Building Stripe's API
amfeng
35
52k
Other Decks in Programming
See All in Programming
監視 やばい
syossan27
7
5.6k
タイムゾーンの奥地は思ったよりも闇深いかもしれない
suguruooki
1
670
Do Dumb Things
mitsuhiko
0
440
Youtube Lofier - Chrome拡張開発
ninikoko
0
2.4k
AIコーディングの理想と現実
tomohisa
22
30k
KawaiiLT 登壇資料 キャリアとモチベーション
hiiragi
0
110
Empowering Developers with HTML-Aware ERB Tooling @ RubyKaigi 2025, Matsuyama, Ehime
marcoroth
2
690
状態と共に暮らす:ステートフルへの挑戦
ypresto
1
600
アプリを起動せずにアプリを開発して品質と生産性を上げる
ishkawa
0
2.8k
AIコードエディタの基盤となるLLMのFlutter性能評価
alquist4121
0
210
Unlock the Potential of Swift Code Generation
rockname
0
260
SEAL - Dive into the sea of search engines - Symfony Live Berlin 2025
alexanderschranz
1
140
Featured
See All Featured
Building a Scalable Design System with Sketch
lauravandoore
462
33k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
5
520
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
104
19k
Gamification - CAS2011
davidbonilla
81
5.2k
The World Runs on Bad Software
bkeepers
PRO
67
11k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Practical Orchestrator
shlominoach
186
11k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
BBQ
matthewcrist
88
9.6k
Speed Design
sergeychernyshev
29
900
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
45
9.5k
YesSQL, Process and Tooling at Scale
rocio
172
14k
Transcript
MOVE FAST, DON'T BREAK YOUR API AMBER FENG @amfeng
None
LET'S BUILD AN API!
post '/v1/charges' do card_number = params[:card_number] amount = params[:amount] charge
= create_charge(card_number, amount) json { id: charge.id, amount: charge.amount card_number: charge.redacted_card_number, success: charge.success } end
post '/v1/charges' do ... unless card_number.length == 16 return {error:
"Invalid card number."} end unless amount > 0 and amount <= CHARGE_MAX return {error: "Invalid amount."} end ... end
post '/v1/charges' do api_key = get_api_key user = User.find_by_key(api_key) unless
user return {error: "Invalid API key."} end ... end
curl https://stripe.com/v1/charges -u API_KEY -d card_number=4242424242424242 -d amount=100 => {
id: "ch_xxx", amount: 100, card_number: "*4242", success: true }
None
WHAT NEXT?
WHAT NEXT? MORE ENDPOINTS
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY MORE CHANGES
WHAT NEXT? MORE ENDPOINTS MORE FUNCTIONALITY MORE CHANGES MORE PROBLEMS
LARGE, TANGLED CODEBASE
COPY-PASTA
ERROR-PRONE DEPENDENCIES
"CRAP, I FORGOT TO UPDATE THE DOCS!" — Everyone ever
HOW DO WE MAKE IT BETTER?
DESIGN FOR YOURSELF, TOO
SEPARATE DIFFERENT LAYERS OF LOGIC
Authentication Validation Endpoint-specific logic Constructing the response Error handling
Error handler Authenticator API logic
use ErrorHandler use Authenticator get '/v1/charges/:id' do user = env.user
id = params[:id] unless user.get_charge(id) raise UserError.new("No charge #{id}!") end end
APIMethods & APIResources
class ChargeCreateMethod < AbstractAPIMethod required :amount, :integer required :card_number, :string
resource ChargeAPIResource def execute create_charge(amount, card_number) end end
class ChargeAPIResource < AbstractAPIResource required :id, :string required :amount, :integer
required :card_number, :string required :success, :boolean def describe_card_number charge.redacted_card_number end end
post '/v1/charges' do APIMethod::ChargeCreateMethod.invoke end get '/v1/charges' do APIMethod::ChargeRetrieveMethod.invoke end
MAKE IT HARD TO MESS UP
class ChargeCreateMethod < AbstractAPIMethod required :amount, :integer required :card_number, :string
document :amount, "Amount, in cents." document :card_number, "The card number." ... end
HIDE BACKWARDS COMPATIBILITY
<todo: tweet>
def execute if !user.version_1? && params[:amount] raise UserError.new("Invalid param.") end
... if !user.version_1? response.delete(:amount) end end
GATES
- :version: 2014-09-24 :new_gates: - :gate: allows_amount :description: >- Sending
amount is now deprecated.
def execute if !user.gating(:allows_amount) && params[:amount] raise UserError.new("Invalid param.") end
... if !user.gating(:allows_amount) response.delete(:amount) end end
COMPATIBILITY LAYERS
Request compatibility API logic Construct API response Response compatibility
IN THE REAL WORLD
106 ENDPOINTS 65 VERSIONS 6 API CLIENTS
DESIGN FOR YOURSELF: SEPARATE LAYERS OF LOGIC MAKE IT HARD
TO MESS UP HIDE BACKWARDS COMPAT
WHAT ELSE?
NOT SURE. (WE'RE STILL FIGURING IT OUT AS WE GO.)
THANKS! (: @amfeng