Slide 1

Slide 1 text

Hashrocket Lunch & Learn #2 Avdi Grimm 2012-04-19

Slide 2

Slide 2 text

Building Libraries that Don’t Suck Distribution Coding API Design Error API Design

Slide 3

Slide 3 text

Distribution

Slide 4

Slide 4 text

Limit dependencies Yes, ActiveSupport is the bee’s knees . . . but it may break someone’s codebase

Slide 5

Slide 5 text

Semantic Versioning Patch versions: fix defects. Minor versions: add features; never change existing (correct) behavior. Major version: break backwards compatibility Make the tiddly-waka (~>) count for something!

Slide 6

Slide 6 text

Don’t name it after a pattern That’s just confusing.

Slide 7

Slide 7 text

Coding

Slide 8

Slide 8 text

No monkey patching! (Unless that’s the purpose of the library)

Slide 9

Slide 9 text

No classes named “Base” Don’t make grepping any harder than it needs to be. Identify the concept, and name it. (ActiveRecord::Record, anyone?) Exceptions: Libraries involving baseball Libraries about base jumping Libraries concerned with Swedish 90s pop group Ace of Base

Slide 10

Slide 10 text

Don’t eat client exceptions module SWS def with_sandwich # ... yield sandwich rescue => e # OM NOM NOM EXCEPTIONS! nil end end

Slide 11

Slide 11 text

Don’t eat client exceptions # Meanwhile, in client code SWS.with_sandwich do # ... some_method_that_raises_a_runtime_error # ... end

Slide 12

Slide 12 text

API design

Slide 13

Slide 13 text

Block-style configuration SWS.configure do |config| conf.selleck_shirtless = true conf.sandwich_type = :baloney conf.waterfall_height *= 2 end

Slide 14

Slide 14 text

Block-style configuration Code-as-config for ultimate flexibility Discoverable: default values can be queried. A convenient point to add validations Can be called more than once

Slide 15

Slide 15 text

Top-level Facade # Instead of: SWS::Sandwich.new(...) # Try: SWS.new_sandwich(...)

Slide 16

Slide 16 text

Top-level Facade Top-level API acts as a reference Doesn’t tie client code to the structure of your library Gives you more leeway to move things around internally Or proliferate classes

Slide 17

Slide 17 text

Error API

Slide 18

Slide 18 text

Exception Classes Logic Error: Library has a defect. Client Error: Client mis-used the library. Transient Failure: Some external dependency isn’t working right now.

Slide 19

Slide 19 text

Exception Tagging with Modules module SWS::Error; end # ... def upload_selleck_to_waterfall # ... rescue HTTPError => e e.extend(SWS::Error) raise end

Slide 20

Slide 20 text

Exception Tagging with Modules # Client code interested in HTTP errors def foo upload_selleck_to_waterfall rescue HTTPError => e log_http_error(e) end

Slide 21

Slide 21 text

Exception Tagging with Modules # Client code concerned with SWS errors def foo upload_selleck_to_waterfall rescue SWS::Error # ignore SWS errors end

Slide 22

Slide 22 text

No-raise API request.on_complete do |response| if response.success? # hell yeah elsif response.timed_out? # aw hell no log( "got a time out" ) elsif response.code == 0 # Could not get an http response log(response.curl_error_message) else # Received a non-successful http response. log( "request failed: " + response.code.to_s) end end

Slide 23

Slide 23 text

Tiered Architecture For wrapping services / libraries Layer 1: Thinnest possible wrapper Typically procedural Stateless Minimal “magic” Layer 2: Object model Stateful

Slide 24

Slide 24 text

Tiered Architecture Evolution Layer 1: Thinnest possible wrapper Layer 2: Glue Layer 3: Object model

Slide 25

Slide 25 text

Your Turn