b b a t s o v / c l o j u r e - s t y l e - g u i d e P U B L I C g Code Code Net work Net work Pull Request s Pull Request s Issues Issues Graphs Graphs ++ 11 13 13 Re ad- Onl y access A community coding style guide for the Clojure programming language Read more ZIP ZIP SSH SSH Gi t Re ad- Onl y Gi t Re ad- Onl y https://github.com/bbatsov/clojure-style-guide.git Tags yy mast er mast er 1 133 commit s latest commit cc0f61724c Add an opening quote bbat sov authored 12 days ago _ CONTRIBUTING.md 5 months ago Added contribution guidelines. [bbatsov] _ README.md 12 days ago Add an opening quote [bbatsov] README.md README.md Role models are import ant . clojure-style-guide / _ The Clojure Style Guide HTTP HTTP 4 4 9 32 Search or type a command Thi s re posi t ory Thi s re posi t ory @@ PDFmyURL.com -- Of f icer Alex J. Murphy / RoboCop This Clojure st yle guide recommends best pract ices so t hat real-world Clojure programmers can writ e code t hat can be maint ained by ot her real-world Clojure programmers. A st yle guide t hat ref lect s real-world usage get s used, and a st yle guide t hat holds t o an ideal t hat has been reject ed by t he people it is supposed t o help risks not get t ing used at all no mat t er how good it is. The guide is separat ed int o several sect ions of relat ed rules. I've t ried t o add t he rat ionale behind t he rules (if it 's omit t ed I've assumed t hat is pret t y obvious). I didn't come up wit h all t he rules out of nowhere - t hey are most ly based on my ext ensive career as a prof essional sof t ware engineer, f eedback and suggest ions f rom members of t he Clojure communit y, and various highly regarded Clojure programming resources, such as "Clojure Programming" and "The Joy of Clojure". The guide is st ill a work in progress - some sect ions are missing, ot hers are incomplet e, some rules are lacking examples, some rules don't have examples t hat illust rat e t hem clearly enough. In due t ime t hese issues will be addressed - just keep t hem in mind f or now. You can generat e a PDF or an HTML copy of t his guide using Transmut er. Source Code Layout & Organizat ion Synt ax Naming Collect ions Mut at ion St rings Except ions Table of Contents PDFmyURL.com Macros Comment s Comment Annot at ions Exist ent ial Tooling Nearly everybody is convinced t hat every st yle but t heir own is ugly and unreadable. Leave out t he "but t heir own" and t hey're probably right ... -- Jerry Cof f in (on indent at ion) Use t wo spaces per indent at ion level. No hard t abs. ;; good (when something (something-else)) ;; bad - four spaces (when something (something-else)) Align vert ically f unct ion argument s. ;; good (filter even? (range 1 10)) ;; bad (filter even? (range 1 10)) Source Code Layout & Organization PDFmyURL.com Align let bindings and map keywords. ;; good (let [thing1 "some stuff" thing2 "other stuff"] {:thing1 thing1 :thing2 thing2}) ;; bad (let [thing1 "some stuff" thing2 "other stuff"] {:thing1 thing1 :thing2 thing2}) Opt ionally omit t he new line bet ween t he f unct ion name and argument vect or f or defn when t here is no docst ring. ;; good (defn foo [x] (bar x)) ;; good (defn foo [x] (bar x)) ;; bad (defn foo [x] (bar x)) Opt ionally omit t he new line bet ween t he argument vect or and a short f unct ion body. ;; good (defn foo [x] (bar x)) PDFmyURL.com ;; good for a small function body (defn foo [x] (bar x)) ;; good for multi-arity functions (defn foo ([x] (bar x)) ([x y] (if (predicate? x) (bar x) (baz x)))) ;; bad (defn foo [x] (if (predicate? x) (bar x) (baz x))) Indent each line of mult i-line docst rings. ;; good (defn foo "Hello there. This is a multi-line docstring." [] (bar)) ;; bad (defn foo "Hello there. This is a multi-line docstring." [] (bar)) Use Unix-st yle line endings. (*BSD/Solaris/Linux/OSX users are covered by def ault , Windows users have t o be ext ra PDFmyURL.com caref ul.) If you're using Git you might want t o add t he f ollowing conf igurat ion set t ing t o prot ect your project f rom Windows line endings creeping in: $ git config --global core.autocrlf true If any t ext precedes an opening bracket ( ( , { and [ ) or f ollows a closing bracket ( ) , } and ] ), separat e t hat t ext f rom t hat bracket wit h a space. Conversely, leave no space af t er an opening bracket and bef ore f ollowing t ext , or af t er preceding t ext and bef ore a closing bracket . ;; good (foo (bar baz) quux) ;; bad (foo(bar baz)quux) (foo ( bar baz ) quux) Don't use commas bet ween t he element s of sequent ial collect ion lit erals. ;; good [1 2 3] (1 2 3) ;; bad [1, 2, 3] (1, 2, 3) Consider enhancing t he readabilit y of map lit erals via judicious use of commas and line breaks. ;; good {:name "Bruce Wayne" :alter-ego "Batman"} PDFmyURL.com ;; good and arguably a bit more readable {:name "Bruce Wayne" :alter-ego "Batman"} ;; good and arguably more compact {:name "Bruce Wayne", :alter-ego "Batman"} Place all t railing parent heses on a single line. ;; good (when something (something-else)) ;; bad (when something (something-else) ) Use empt y lines bet ween t op-level f orms. ;; good (def x ...) (defn foo ...) ;; bad (def x ...) (defn foo ...) An except ion t o t he rule is t he grouping of relat ed def s t oget her. ;; good (def min-rows 10) (def max-rows 20) (def min-cols 15) PDFmyURL.com (def max-cols 30) Do not place blank lines in t he middle of a f unct ion or macro def init ion. An except ion can be made t o indicat e grouping of pairwise const ruct s as f ound in e.g. let and cond . Where f easible, avoid making lines longer t han 80 charact ers. Avoid t railing whit espace. Use one f ile per namespace. St art every namespace wit h a comprehensive ns f orm, comprised of import s, require s, refer s and use s. (ns examples.ns (:refer-clojure :exclude [next replace remove]) (:require (clojure [string :as string] [set :as set]) [clojure.java.shell :as sh]) (:use (clojure zip xml)) (:import java.util.Date java.text.SimpleDateFormat (java.util.concurrent Executors LinkedBlockingQueue))) Avoid single-segment namespaces. ;; good (ns example.ns) ;; bad (ns example) Avoid t he use of overly long namespaces(i.e. wit h more t han 5 segment s). PDFmyURL.com Avoid f unct ions longer t han 10 LOC (lines of code). Ideally, most f unct ions will be short er t han 5 LOC. Avoid paramet er list s wit h more t han t hree or f our posit ional paramet ers. Avoid t he use of namespace-manipulat ing f unct ions like require and refer . They are ent irely unnecessary out side of a REPL environment . Use declare t o enable f orward ref erences. Pref er higher-order f unct ions like map t o loop/recur . Pref er f unct ion pre and post condit ions t o checks inside a f unct ion's body. ;; good (defn foo [x] {:pre [(pos? x)]} (bar x)) ;; bad (defn foo [x] (if (pos? x) (bar x) (throw (IllegalArgumentException "x must be a positive number!"))) Don't def ine vars inside f unct ions. ;; very bad (defn foo [] (def x 5) ...) Syntax PDFmyURL.com Don't shadow clojure.core names wit h local bindings. ;; bad - you're forced to used clojure.core/map fully qualified inside (defn foo [map] ...) Use seq as a t erminat ing condit ion t o t est whet her a sequence is empt y (t his t echnique is somet imes called nil punning). ;; good (defn print-seq [s] (when (seq s) (prn (first s)) (recur (rest s)))) ;; bad (defn print-seq [s] (when-not (empty? s) (prn (first s)) (recur (rest s)))) Use when inst ead of (if ... (do ...) . ;; good (when pred (foo) (bar)) ;; bad (if pred (do (foo) (bar))) PDFmyURL.com Use if-let inst ead of let + if . ;; good (if-let [result (foo x)] (something-with result) (something-else)) ;; bad (let [result (foo x)] (if result (something-with result) (something-else))) Use when-let inst ead of let + when . ;; good (when-let [result (foo x)] (do-something-with result) (do-something-more-with result)) ;; bad (let [result (foo x)] (when result (do-something-with result) (do-something-more-with result))) Use if-not inst ead of (if (not ...) ...) . ;; good (if-not (pred) (foo)) ;; bad (if (not pred) (foo)) PDFmyURL.com Use when-not inst ead of (when (not ...) ...) . ;; good (when-not pred (foo) (bar)) ;; bad (when (not pred) (foo) (bar)) Use when-not inst ead of (if-not ... (do ...) . ;; good (when-not pred (foo) (bar)) ;; bad (if-not pred (do (foo) (bar))) Use not= inst ead of (not (= ...)) . ;; good (not= foo bar) ;; bad (not (= foo bar)) Pref er % over %1 in f unct ion lit erals wit h only one paramet er. PDFmyURL.com Pref er % over %1 in f unct ion lit erals wit h only one paramet er. ;; good #(Math/round %) ;; bad #(Math/round %1) Pref er %1 over % in f unct ion lit erals wit h more t han one paramet er. ;; good #(Math/pow %1 %2) ;; bad #(Math/pow % %2) Don't wrap f unct ions in anonymous f unct ions when you don't need t o. ;; good (filter even? (range 1 10)) ;; bad (filter #(even? %) (range 1 10)) Don't use f unct ion lit erals if t he f unct ion body will consist of more t han one f orm. ;; good (fn [x] (println x) (* x 2)) ;; bad (you need an explicit do form) #(do (println %) (* % 2)) PDFmyURL.com Favor t he use of complement versus t he use of an anonymous f unct ion. ;; good (filter (complement some-pred?) coll) ;; bad (filter #(not (some-pred? %)) coll) This rule should obviously be ignored if t he complement ing predicat e exist s in t he f orm of a separat e f unct ion (e.g. even? and odd? ). Leverage comp when it would yield simpler code. ;; Assuming `(:require [clojure.string :as str])`... ;; good (map #(str/capitalize (str/trim %)) ["top " " test "]) ;; better (map (comp str/capitalize str/trim) ["top " " test "]) Leverage partial when it would yield simpler code. ;; good (map #(+ 5 %) (range 1 10)) ;; (arguably) better (map (partial + 5) (range 1 10)) Pref er t he use of t he t hreading macros -> (t hread-f irst ) and ->> (t hread-last ) t o heavy f orm nest ing. ;; good (-> [1 2 3] reverse PDFmyURL.com (conj 4) prn) ;; not as good (prn (conj (reverse [1 2 3]) 4)) ;; good (->> (range 1 10) (filter even?) (map (partial * 2))) ;; not as good (map (partial * 2) (filter even? (range 1 10))) Pref er .. t o -> when chaining met hod calls in Java int erop. ;; good (-> (System/getProperties) (.get "os.name")) ;; better (.. System getProperties (get "os.name")) Use :else as t he cat ch-all t est expression in cond and condp . ;; good (cond (< n 0) "negative" (> n 0) "positive" :else "zero")) ;; bad (cond (< n 0) "negative" PDFmyURL.com (> n 0) "positive" true "zero")) Pref er condp inst ead of cond when t he predicat e & expression don't change. ;; good (cond (= x 10) :ten (= x 20) :twenty (= x 30) :forty :else :dunno) ;; much better (condp = x 10 :ten 20 :twenty 30 :forty :dunno) Pref er case inst ead of cond or condp when t est expressions are compile-t ime const ant s. ;; good (cond (= x 10) :ten (= x 20) :twenty (= x 30) :forty :else :dunno) ;; better (condp = x 10 :ten 20 :twenty 30 :forty :dunno) PDFmyURL.com ;; best (case x 10 :ten 20 :twenty 30 :forty :dunno) Use short f orms in cond and relat ed. If not possible give visual hint s f or t he pairwise grouping wit h comment s or empt y lines. ;; good (cond (test1) (action1) (test2) (action2) :else (default-action)) ;; ok-ish (cond ;; test case 1 (test1) (long-function-name-which-requires-a-new-line (complicated-sub-form (-> 'which-spans multiple-lines))) (test2) (another-very-long-function-name (yet-another-sub-form (-> 'which-spans multiple-lines))) :else (the-fall-through-default-case (which-also-spans 'multiple 'lines))) PDFmyURL.com Use a set as a predicat e when appropriat e. ;; good (remove #{0} [0 1 2 3 4 5]) ;; bad (remove #(= % 0) [0 1 2 3 4 5]) ;; good (count (filter #{\a \e \i \o \u} "mary had a little lamb")) ;; bad (count (filter #(or (= % \a) (= % \e) (= % \i) (= % \o) (= % \u)) "mary had a little lamb")) Use (inc x) & (dec x) inst ead of (+ x 1) and (- x 1) . Use (pos? x) , (neg? x) & (zero? x) inst ead of (> x 0) , (< x 0) & (= x 0) . Use t he sugared Java int erop f orms. ;;; object creation ;; good (java.util.ArrayList. 100) ;; bad (new java.util.ArrayList 100) ;;; static method invocation ;; good (Math/pow 2 10) PDFmyURL.com ;; bad (. Math pow 2 10) ;;; instance method invocation ;; good (.substring "hello" 1 3) ;; bad (. "hello" substring 1 3) ;;; static field access ;; good Integer/MAX_VALUE ;; bad (. Integer MAX_VALUE) ;;; instance field access ;; good (.someField some-object) ;; bad (. some-object some-field) Use t he compact met adat a not at ion f or met adat a t hat cont ains only slot s whose keys are keywords and whose value is boolean true . ;; good (def ^:private a 5) ;; bad (def ^{:private true} a 5) Denot e privat e part s of your code. PDFmyURL.com ;; good (defn- private-fun [] ...) (def ^:private private-var ...) ;; bad (defn private-fun [] ...) ; not private at all (defn ^:private private-fun [] ...) ; overly verbose (def private-var ...) ; not private at all Be caref ul regarding what exact ly do you at t ach met adat a t o. ;; we attach the metadata to the var referenced by `a` (def ^:private a {}) (meta a) ;=> nil (meta #'a) ;=> {:private true} ;; we attach the metadata to the empty hash-map value (def a ^:private {}) (meta a) ;=> {:private true} (meta #'a) ;=> nil The only real dif f icult ies in programming are cache invalidat ion and naming t hings. -- Phil Karlt on When naming namespaces f avor t he f ollowing t wo schemas: project.module Naming PDFmyURL.com organization.project.module Use lisp-case in composit e namespace segment s(e.g. bruce.project-euler ) Use lisp-case f or f unct ion and variable names. ;; good (def some-var ...) (defn some-fun ...) ;; bad (def someVar ...) (defn somefun ...) (def some_fun ...) Use CamelCase f or prot ocols, records, st ruct s, and t ypes. (Keep acronyms like HTTP, RFC, XML uppercase.) The names of predicat e met hods (met hods t hat ret urn a boolean value) should end in a quest ion mark. (i.e. even? ). ;; good (defn palindrome? ...) ;; bad (defn palindrome-p ...) ; Common Lisp style (defn is-palindrome ...) ; Java style The names of f unct ions/macros t hat are not saf e in STM t ransact ions should end wit h an exclamat ion mark. (i.e. reset! ) Use -> inst ead of to in t he names of conversion f unct ions. ;; good (defn f->c ...) ;; not so good PDFmyURL.com (defn f-to-c ...) Use *earmuffs* f or t hings int ended f or rebinding (ie. are dynamic). ;; good (def ^:dynamic *a* 10) ;; bad (def ^:dynamic a 10) Don't use a special not at ion f or const ant s; everyt hing is assumed a const ant unless specif ied ot herwise. Use _ f or dest ruct uring t arget s and f ormal argument s names whose value will be ignored by t he code at hand. ;; good (let [[a b _ c] [1 2 3 4]] (println a b c)) (dotimes [_ 3] (println "Hello!")) ;; bad (let [[a b c d] [1 2 3 4]] (println a b d)) (dotimes [i 3] (println "Hello!")) Follow clojure.core 's example f or idiomat ic names like pred and coll . in f unct ions: f , g , h - f unct ion input n - int eger input usually a size index - int eger index PDFmyURL.com x , y - numbers s - st ring input coll - a collect ion pred - a predicat e closure & more - variadic input in macros: expr - an expression body - a macro body binding - a macro binding vect or It is bet t er t o have 100 f unct ions operat e on one dat a st ruct ure t han t o have 10 f unct ions operat e on 10 dat a st ruct ures. -- Alan J. Perlis Avoid t he use of list s f or generic dat a st orage (unless a list is exact ly what you need). Pref er t he use of keywords f or hash keys. ;; good {:name "Bruce" :age 30} ;; bad {"name" "Bruce" "age" 30} Pref er t he use of t he lit eral collect ion synt ax where applicable. However, when def ining set s, only use lit eral synt ax when t he values are compile-t ime const ant s. Collections PDFmyURL.com ;; good [1 2 3] #{1 2 3} (hash-set (func1) (func2)) ; values determined at runtime ;; bad (vector 1 2 3) (hash-set 1 2 3) #{(func1) (func2)} ; will throw runtime exception if (func1) = (func2) Avoid accessing collect ion members by index whenever possible. Pref er t he use of keywords as f unct ions f or ret rieving values f rom maps, where applicable. (def m {:name "Bruce" :age 30}) ;; good (:name m) ;; more verbose than necessary (get m :name) ;; bad - susceptible to NullPointerException (m :name) Leverage t he f act t hat most collect ions are f unct ions of t heir element s. ;; good (filter #{\a \e \o \i \u} "this is a test") ;; bad - too ugly to share Leverage t he f act t hat keywords can be used as f unct ions of a collect ion. ((juxt :a :b) {:a "ala" :b "bala"}) PDFmyURL.com Avoid t he use of t ransient collect ions, except f or perf ormance-crit ical port ions of t he code. Avoid t he use of Java collect ions. Avoid t he use of Java arrays, except f or int erop scenarios and perf ormance-crit ical code dealing heavily wit h primit ive t ypes. Consider wrapping all I/O calls wit h t he io! macro t o avoid nast y surprises if you accident ally end up calling such code in a t ransact ion. Avoid t he use of ref-set whenever possible. (def r (ref 0)) ;; good (dosync (alter r + 5)) ;; bad (dosync (ref-set r 5)) Try t o keep t he size of t ransact ions (t he amount of work encapsulat ed in t hem) as small as possible. Avoid having bot h short - and long-running t ransact ions int eract ing wit h t he same Ref . Mutation Refs Agents PDFmyURL.com Use send only f or act ions t hat are CPU bound and don't block on I/O or ot her t hreads. Use send-off f or act ions t hat might block, sleep, or ot herwise t ie up t he t hread. Avoid at om updat es inside STM t ransact ions. Try t o use swap! rat her t han reset! , where possible. (def a (atom 0)) ;; good (swap! a + 5) ;; not as good (reset! a 5) Pref er st ring manipulat ion f unct ions f rom clojure.string over Java int erop or rolling your own. ;; good (clojure.string/upper-case "bruce") ;; bad (.toUpperCase "bruce") Atoms Strings Exceptions PDFmyURL.com Reuse exist ing except ion t ypes. Idiomat ic Clojure code, when it does t hrow an except ion, t hrows an except ion of a st andard t ype (e.g. java.lang.IllegalArgumentException , java.lang.UnsupportedOperationException , java.lang.IllegalStateException , java.io.IOException ). Favor with-open over finally . Don't writ e a macro if a f unct ion will do. Creat e an example of a macro usage f irst and t he macro af t erwards. Break complicat ed macros int o smaller f unct ions whenever possible. A macro should usually just provide synt act ic sugar and t he core of t he macro should be a plain f unct ion. Doing so will improve composabilit y. Pref er synt ax-quot ed f orms over building list s manually. Good code is it s own best document at ion. As you're about t o add a comment , ask yourself , "How can I improve t he code so t hat t his comment isn't needed?" Improve t he code and t hen document it t o make it even clearer. -- St eve McConnell Endeavor t o make your code as self -document ing as possible. Writ e heading comment s wit h at least f our semicolons. Writ e t op-level comment s wit h t hree semicolons. Writ e comment s on a part icular f ragment of code bef ore t hat f ragment and aligned wit h it , using t wo semicolons. Macros Comments PDFmyURL.com Writ e margin comment s wit h one semicolon. Always have at least one space bet ween t he semicolon and t he t ext t hat f ollows it . ;;;; Frob Grovel ;;; This section of code has some important implications: ;;; 1. Foo. ;;; 2. Bar. ;;; 3. Baz. (defn fnord [zarquon] ;; If zob, then veeblefitz. (quux zot mumble ; Zibblefrotz. frotz)) Comment s longer t han a word begin wit h a capit al let t er and use punct uat ion. Separat e sent ences wit h one space. Avoid superf luous comment s. ;; bad (inc counter) ; increments counter by one Keep exist ing comment s up-t o-dat e. An out dat ed comment is worse t han no comment at all. Pref er t he use of t he #_ reader macro over a regular comment when you need t o comment out a part icular f orm. ;; good (+ foo #_(bar x) delta) ;; bad (+ foo ;; (bar x) PDFmyURL.com delta) Good code is like a good joke - it needs no explanat ion. -- Russ Olsen Avoid writ ing comment s t o explain bad code. Ref act or t he code t o make it self -explanat ory. ("Do, or do not . There is no t ry." --Yoda) Annot at ions should usually be writ t en on t he line immediat ely above t he relevant code. The annot at ion keyword is f ollowed by a colon and a space, t hen a not e describing t he problem. If mult iple lines are required t o describe t he problem, subsequent lines should be indent ed as much as t he f irst one. Tag t he annot at ion wit h your init ials and a dat e so it s relevance can be easily verif ied. (defn some-fun [] ;; FIXME: This has crashed occasionally since v1.2.3. It may ;; be related to the BarBazUtil upgrade. (xz 13-1-31) (baz)) In cases where t he problem is so obvious t hat any document at ion would be redundant , annot at ions may be lef t at t he end of t he of f ending line wit h no not e. This usage should be t he except ion and not t he rule. (defn bar [] (sleep 100)) ; OPTIMIZE Use TODO t o not e missing f eat ures or f unct ionalit y t hat should be added at a lat er dat e. Use FIXME t o not e broken code t hat needs t o be f ixed. Comment Annotations PDFmyURL.com Use OPTIMIZE t o not e slow or inef f icient code t hat may cause perf ormance problems. Use HACK t o not e "code smells" where quest ionable coding pract ices were used and should be ref act ored away. Use REVIEW t o not e anyt hing t hat should be looked at t o conf irm it is working as int ended. For example: REVIEW: Are we sure this is how the client does X currently? Use ot her cust om annot at ion keywords if it f eels appropriat e, but be sure t o document t hem in your project 's README or similar. Code in a f unct ional way, avoiding mut at ion when t hat makes sense. Be consist ent . In an ideal world, be consist ent wit h t hese guidelines. Use common sense. There are some t ools creat ed by t he Clojure communit y t hat might aid you in your endeavor t o writ e idiomat ic Clojure code. Slamhound is a t ool t hat will aut omat ically generat e proper ns declarat ions f rom your exist ing code. kibit is a st at ic code analyzer f or Clojure which uses core.logic t o search f or pat t erns of code f or which t here might exist a more idiomat ic f unct ion or macro. Existential Tooling PDFmyURL.com Not hing writ t en in t his guide is set in st one. It 's my desire t o work t oget her wit h everyone int erest ed in Clojure coding st yle, so t hat we could ult imat ely creat e a resource t hat will be benef icial t o t he ent ire Clojure communit y. Feel f ree t o open t icket s or send pull request s wit h improvement s. Thanks in advance f or your help! This work is licensed under a Creat ive Commons At t ribut ion 3.0 Unport ed License A communit y-driven st yle guide is of lit t le use t o a communit y t hat doesn't know about it s exist ence. Tweet about t he guide, share it wit h your f riends and colleagues. Every comment , suggest ion or opinion we get makes t he guide just a lit t le bit bet t er. And we want t o have t he best possible guide, don't we? Cheers, Bozhidar Contributing License Spread the Word PDFmyURL.com Git Hub Git Hub About us About us Blog Blog Contact & support Contact & support GitHub Enterprise GitHub Enterprise Site status Site status Applicat ions Applicat ions GitHub for Mac GitHub for Mac GitHub for Windows GitHub for Windows GitHub for Eclipse GitHub for Eclipse GitHub mobile apps GitHub mobile apps Services Services Gauges: Web analytics Gauges: Web analytics Speaker Deck: Presentations Speaker Deck: Presentations Gist: Code snippets Gist: Code snippets Job board Job board Document at ion Document at ion GitHub Help GitHub Help Developer API Developer API GitHub Flavored Markdown GitHub Flavored Markdown GitHub Pages GitHub Pages More More Training Training Students & teachers Students & teachers The Shop The Shop Plans & pricing Plans & pricing The Octodex The Octodex 2013 2013 GitHub GitHub, Inc. All rights reserved. , Inc. All rights reserved. Terms of Service Terms of Service Privacy Privacy Security Security [[ PDFmyURL.com