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.8k
Building Stripe's API
amfeng
35
52k
Other Decks in Programming
See All in Programming
すべてのコンテキストを、 ユーザー価値に変える
applism118
2
820
童醫院敏捷轉型的實踐經驗
cclai999
0
190
なんとなくわかった気になるブロックテーマ入門/contents.nagoya 2025 6.28
chiilog
1
220
なぜ「共通化」を考え、失敗を繰り返すのか
rinchoku
1
530
アンドパッドの Go 勉強会「 gopher 会」とその内容の紹介
andpad
0
260
#kanrk08 / 公開版 PicoRubyとマイコンでの自作トレーニング計測装置を用いたワークアウトの理想と現実
bash0c7
1
460
なぜ適用するか、移行して理解するClean Architecture 〜構造を超えて設計を継承する〜 / Why Apply, Migrate and Understand Clean Architecture - Inherit Design Beyond Structure
seike460
PRO
1
690
プロダクト志向ってなんなんだろうね
righttouch
PRO
0
160
5つのアンチパターンから学ぶLT設計
narihara
1
110
iOSアプリ開発で 関数型プログラミングを実現する The Composable Architectureの紹介
yimajo
2
210
CursorはMCPを使った方が良いぞ
taigakono
1
180
A2A プロトコルを試してみる
azukiazusa1
2
1.2k
Featured
See All Featured
Faster Mobile Websites
deanohume
307
31k
Build The Right Thing And Hit Your Dates
maggiecrowley
36
2.8k
A Tale of Four Properties
chriscoyier
160
23k
We Have a Design System, Now What?
morganepeng
53
7.7k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
330
24k
A Modern Web Designer's Workflow
chriscoyier
694
190k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.4k
Producing Creativity
orderedlist
PRO
346
40k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
32
2.3k
Become a Pro
speakerdeck
PRO
28
5.4k
Documentation Writing (for coders)
carmenintech
72
4.9k
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