$30 off During Our Annual Pro Sale. View Details »
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.9k
Building Stripe's API
amfeng
35
52k
Other Decks in Programming
See All in Programming
TestingOsaka6_Ozono
o3
0
170
Navigation 3: 적응형 UI를 위한 앱 탐색
fornewid
1
380
チームをチームにするEM
hitode909
0
350
AIコーディングエージェント(skywork)
kondai24
0
190
Context is King? 〜Verifiability時代とコンテキスト設計 / Beyond "Context is King"
rkaga
10
1.3k
Cap'n Webについて
yusukebe
0
140
大規模Cloud Native環境におけるFalcoの運用
owlinux1000
0
160
DevFest Android in Korea 2025 - 개발자 커뮤니티를 통해 얻는 가치
wisemuji
0
160
これだけで丸わかり!LangChain v1.0 アップデートまとめ
os1ma
6
1.9k
マスタデータ問題、マイクロサービスでどう解くか
kts
0
110
Integrating WordPress and Symfony
alexandresalome
0
160
Deno Tunnel を使ってみた話
kamekyame
0
150
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
120
Building Adaptive Systems
keathley
44
2.9k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.2k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.3k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
180
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
63
35k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
680
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
0
67
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Google's AI Overviews - The New Search
badams
0
860
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
10
740
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