The Hikikomori’s Guide to JavaScript

This post has been automatically generated. I use this blog to collect links that I have bookmarked. All activity is automated.

Don’t start with an Application, start with the API. This might seem weird, but it actually makes a lot of sense, because the API defines how other computer things can interact with the data your application manipulates. But hey, why should we leave that as “other computer things”? Let me rephrase:

The API defines how your application (and other computer things) can interact with the data you’re interested in.

My usual design approach is to examine the inputs and outputs of the application, and then design an API that handles such data. Later I’ll stack the user experience and the “glue” code on top of such API to form an actual application.

This fits nicely with the “One Module Does One Thing” approach because then the API defines which kind of data you’re dealing with, and all the meaningful transformations you can apply to such data. Transformations in turn do one thing, and might be grouped logically. Modules are really just a logical group of computations, that it happens to be a file in many languages is just an accident.

For example, let’s say we want to write an application to display Tweets to the user. First and foremost, we examine the inputs and outputs, and draft some types to encode the data in our application (I’m using a type notation inspired by Haskell, but this should be nonetheless pretty straightforward):

-- | A TwitterTweet is something Twitter gives us (the input)
-- (note that this is a stripped down version)
type TwitterTweet
  favorited     :: Boolean
  retweeted     :: Boolean
  retweet_count :: Number

  created_at :: String  -- ^ Serialised Date
  source     :: String
  user       :: User
  id_str     :: String

  in_reply_to_user_id_str   :: String
  in_reply_to_status_id_str :: String
  in_reply_to_screen_name   :: String

  entities   :: { "urls"          :: [Entity]
                , "hashtags"      :: [Entity]
                , "user_mentions" :: [Entity]

  text :: String    

So, the data Twitter gives us is quite a mess, and it’d be really difficult to manipulate that kind of data in our application. We can do better, so let’s define a better type to encode a Tweet:

type User
  name :: String -- ^ The users' screen name
  id   :: String -- ^ Twitter's user ID

type Entity
  url          :: String
  expandedUrl  :: String
  displayUrl   :: String
  indices      :: (Number, Number)

type Text
  plain    :: String
  entities :: { "urls"          :: [Entity]
              , "hashtags"      :: [Entity]
              , "user_mentions" :: [Entity]

type Tweet
  id            :: String -- ^ Unique identifier for this tweet
  user          :: User   -- ^ The user that tweeted this
  inReplyTo     :: Tweet
  source        :: String -- ^ Which application was used to compose this
  date          :: Date   -- ^ When this tweet was crafted
  favourited    :: Boolean
  retweeted     :: Boolean
  retweetCount  :: Number
  text          :: Text

So, now User and Text are separate types, this is because they make sense outside of the context of a Tweet and we might want to manipulate them separately. There’s no reason to provide a complected type to a function that only needs to know the name of a user, for example.

Once we’re done with the types our application needs to manipulate, we can draft an API that provides the primitives to manipulate these types, given the operations we’ll be applying to them and the output.

-- * Type conversions

-- | We need to convert from Twitter format to ours
normaliseTweet :: TwitterTweet -> Tweet

-- | Convert Twitter Date serialisation to actual DateTime
parseDate :: String -> Date

-- | Extract the User that composed the tweet
twittedBy :: TwitterText -> User

-- | Extract reply information
repliedToUser :: TwitterText -> User
repliedToTweet :: TwitterText -> Tweet

-- | Extract the Text
textFor :: TwitterText -> Text

-- * Display transformations
-- (These are application-specific because they only make sense in the
-- context of user-facing HTML pages)

-- | We want to display a Tweet as HTML
renderTweet :: Tweet -> HTML

-- | We want to display a Text as HTML
textToHTML :: Text -> HTML

-- | We want to know the relative time since the tweet
fromNow :: Date -> String

-- | We want to display a link to a User
linkToUser :: User -> HTML

-- | We also want to display a link to a Tweet
linkToTweet :: Tweet -> HTML

If there’s one hint I can provide when doing the initial API design, it would be:

Extract the most basic, orthogonal concepts, and provide primitives to encode them.

You can always add combinators on top of those minimal and simple primitives to let them do more stuff. Working with reeeeally small set of primitives and a lot of combinators means you get to write simple code that actually scales! But then, picking the right primitives can be really hard at times, so you need to have a good deal of knowledge about the domain you’re trying to encode in your API.

via Echo JS

Leave a comment

Filed under Auto

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s