Don't Make Me Read Your Mind

Don't Make Me Read Your Mind

Talk given at CICONF 2012 in San Francisco, CA

2c91f8dbf323072a564dbc4d97a0b627?s=128

Aaron Kuzemchak

August 12, 2012
Tweet

Transcript

  1. Don't Make Me Read Your Mind Aaron Kuzemchak CICONF 2012

    - San Francisco, CA
  2. Goal ๏ To suggest best practices and tools to make

    your code easier and more enjoyable for future developers to understand.
  3. About Me

  4. Sr. Application Developer at Visual Chefs

  5. I'm from Richmond, VA

  6. I have an awesome family

  7. Former full-time front-end developer

  8. I — PHP

  9. I build... ๏ Websites with ExpressionEngine ๏ Web applications with

    CodeIgniter & Laravel
  10. ๏ http://kuzemchak.net/ ๏ https://github.com/akuzemchak ๏ @akuzemchak My info

  11. About Visual Chefs

  12. Also known as "eecoder"

  13. Also from Richmond, VA

  14. We rock ExpressionEngine

  15. We also rock CodeIgniter

  16. ๏ http://www.visualchefs.com/ ๏ http://eecoder.com/ ๏ @eecoder Our info

  17. Overview 1.Writing documentation 2.Organization & planning 3.Tools to help

  18. "Documentation! I'm so excited!" you all say

  19. None
  20. It's important if anyone else will ever look at your

    code
  21. And it can always be done better

  22. And if you don't do it...

  23. Bad things await you Trust me, you want to document

  24. Nothing revolutionary

  25. KYA: Keep You Accountable

  26. KYA: Kick Your Ass

  27. Documentation

  28. That's documentation in your code, not end-user

  29. Everything you write should be documented

  30. I can't read your mind Aha! That's where the presentation

    title comes from!
  31. You and I don't see things the same way

  32. None
  33. Don't assume I'm good enough to learn from your code

  34. Don't assume your code is good enough to teach

  35. Document first

  36. Use your comments to guide you

  37. // ------------------------------------------------------------ // User Login // ------------------------------------------------------------ // If the

    credentials were correct, login and redirect to dashboard // Otherwise, redirect to the login form with errors
  38. // ------------------------------------------------------------ // User Login // ------------------------------------------------------------ // If the

    credentials were correct, login and redirect to dashboard if($this->auth->login($email, $password) === TRUE) { redirect('account/dashboard'); } // Otherwise, redirect to the login form with errors else { $this->session->set_userdata('errors', $this->auth->errors); redirect('login'); }
  39. Comment major control structures

  40. ๏ Classes ๏ Properties ๏ Methods ๏ Functions ๏ Most

    conditionals ๏ Most loops
  41. Tag your comments

  42. // @TODO: Handle failed API requests better // @NOTE: Passing

    by reference is necessary here // @HACK: Fixed minor bug in query builder // @SEE: http://tinyurl.com/2g9mqh // @BLAME: Alex royally screwed this up
  43. Write good docblocks

  44. /** * login */ public function login($email, $password) { //

    @TODO }
  45. /** * login */ public function login($email, $password) { //

    @TODO } NO!
  46. ๏ What it is ๏ What it does ๏ What

    it accepts ๏ What it returns Good docblocks tell you:
  47. Enables code hinting in some IDEs

  48. Allows easy documentation generation

  49. Classes should describe author and meta info

  50. /** * User account model * * @author Aaron Kuzemchak

    * @copyright 2012 Aaron Kuzemchak * @link http://kuzemchak.net/ * @package CodeIgniter * @subpackage Models */ class User_model extends CI_Model { // @TODO }
  51. Methods should have more functional info

  52. /** * Hashes an array of settings for easy storage

    in a database * * @param array $data * @return string */ public static function hash_settings($data) { return base64_encode(serialize($data)); }
  53. Properties should briefly describe the purpose and type

  54. /** * The user's email address * * @var string

    */ public $email;
  55. Put example usage in the docblock

  56. /** * Hashes an array of settings for easy storage

    in a database * * <code> * $settings = MyClass::hash_settings($data_array); * </code> * * @param array $data * @return string */ public static function hash_settings($data) { return base64_encode(serialize($data)); }
  57. Use type hinting with your arguments

  58. /** * Hashes an array of settings for easy storage

    in a database * * <code> * $settings = MyClass::hash_settings($data_array); * </code> * * @param array $data * @return string */ public static function hash_settings(array $data) { return base64_encode(serialize($data)); }
  59. Don't be afraid to be verbose

  60. // At this point we need to send an email

    to the user to confirm // their purchase. Since it's an HTML email, we've stored it in a // view, and can pass the necessary data to it. $email = $this->load->view('emails/receipt', $data, TRUE);
  61. Reference ticket numbers, user stories, etc.

  62. Because most customer "logic" doesn't make sense

  63. // At this point we send an email reminder to

    the customer, informing them // that their subscription will expire soon. // However, don't send an email if today is a Monday or Thursday, because // the client's "really tech-saavy" cousin told them that people don't // read their email on those days. // @SEE: Ticket #138 - http://projects.visualchefs.com/tickets/138
  64. Functions should have descriptive names

  65. And, they shouldn't try to do too much

  66. /** * Get the number of comments for a user

    * * @param int $user_id * @return int */ public function comment_count($user_id) { // @TODO } /** * Get a user's comments * * @param int $user_id * @param int $limit * @return array */ public function comments($user_id, $limit = 100) { // @TODO }
  67. /** * Get a user's comments, or just a count

    of their comments. * * @param int $user_id * @param int $limit * @param bool $count_only * @return int|array */ public function comments($user_id, $limit = 100, $count_only = FALSE) { // @NOTE: It's difficult to tell what the function does by its name // @NOTE: It also does too many things // @NOTE: I can't remember what the parameters are }
  68. /** * Get a user's comments, or just a count

    of their comments. * * @param int $user_id * @param int $limit * @param bool $count_only * @return int|array */ public function comments($user_id, $limit = 100, $count_only = FALSE) { // @NOTE: It's difficult to tell what the function does by its name // @NOTE: It also does too many things // @NOTE: I can't remember what the parameters are } meh...
  69. CodeIgniter's query builder is a great example

  70. $results = $this->db->select('id, email') ->where('plan_id', 1) ->order_by('id', 'ASC') ->limit(10) ->get('users');

  71. Reference docs for third- party tools

  72. // Set the value in Redis if it doesn't already

    exist // @SEE: http://redis.io/commands/setnx
  73. Summary ๏ Documentation is easier when done first ๏ Give

    as much insight as you can ๏ Write those friggin' docblocks! ๏ Be verbose ๏ Link to external docs when possible
  74. Organization

  75. MVC has made this easier

  76. Have a plan

  77. Write a technical or behavioral document

  78. 1. User fills out the registration form 2. User clicks

    submit 3. Form is submitted via Ajax 1. Form data is validated 1. If valid, account is created 2. JSON response is returned with success flag, and array of errors if invalid data 4. If successful, user is redirected to the dashboard 1. If not successful, list of errors are shown Sample
  79. Helps others get a visual description of what's happening

  80. None
  81. Simplicity is always appreciated

  82. Don't over-engineer things

  83. Keep a README file

  84. You're probably already doing this if you use Github or

    Bitbucket
  85. List technical requirements & setup instructions

  86. REQUIREMENTS ============ * PHP 5.3+ * curl * mcrypt *

    pdo_mysql * mongo PECL extension * MySQL 5.4+ * MongoDB * Redis
  87. SETUP INSTRUCTIONS ================== 1. Create a MySQL database. 2. Update

    settings in local database config file. 3. Run database migrations from the command line.
  88. Point out non-obvious changes/tweaks/hacks

  89. APPLICATION NOTES ================= * Default timezone is set in index.php

    so that I don't have to rely on CI's odd localization system.
  90. Note third-party tools you are using

  91. THIRD-PARTY TOOLS ================= * dompdf - http://code.google.com/p/dompdf

  92. Use a style guide

  93. Makes it easier to get new devs acquainted

  94. CodeIgniter's is a great starting point

  95. None
  96. PSR-1 isn't bad either Unless you despise camelCase

  97. Use the third_party folder

  98. Prevents needing to separate related configs, libraries, models, etc.

  99. third_party auth config auth.php libraries auth.php models user_model.php

  100. $this->load->add_package_path(APPPATH.'third_party/auth'); $this->load->library('auth'); $this->load->model('user_model');

  101. Manage CodeIgniter packages with Sparks

  102. Similar benefits to using the third_party folder

  103. Manage other dependencies with Composer

  104. Useful for any PHP project!

  105. Write tests

  106. Yes, unit tests

  107. But, how about browser- scripted tests as well?

  108. You can watch interactions, and make sure you're not breaking

    stuff
  109. Keep them updated when things change!

  110. Summary ๏ Have a plan ๏ Simplicity ๏ Keep a

    README file ๏ Use a style guide ๏ Keep packages organized with tools like Sparks and Composer ๏ Write tests
  111. Tools

  112. Sparks

  113. Makes managing CodeIgniter packages super easy

  114. cd project-root php -r "$(curl -fsSL http://getsparks.org/go-sparks)" php tools/spark install

    ion_auth
  115. $this->load->spark('ion_auth/2.3.2'); if( ! $this->ion_auth->logged_in()) { redirect('login'); }

  116. It's been around a while, so use if it you

    don't already
  117. Composer

  118. Awesome new dependency manager

  119. For any PHP project

  120. It's not PEAR!

  121. Packages are installed on a per-project basis

  122. cd project-root curl -s https://getcomposer.org/installer | php

  123. A simple JSON file controls your packages

  124. { "require": { "nategood/httpful": ">=1.0" } }

  125. # the first time you run php composer.phar install #

    all other times php composer.phar update
  126. One autoloader to rule them all!

  127. // Put this in index.php require 'vendor/autoload.php'; // Call a

    class and it will be loaded automatically $response = Httpful\Request::get('http://eecoder.com/')->send();
  128. Browse packages at packagist.org

  129. None
  130. ApiGen

  131. Generate documentation from your docblocks

  132. Organized by namespaces and package/subpackage

  133. Beautiful full highlighted source browsing

  134. Beautiful default theme (and you can make your own)

  135. None
  136. Easy to install, easy to run

  137. # download apigen to your project root (or wherever) php

    apigen/apigen.php -s source-folder -d destination-folder # watch the pretty progress bar!
  138. None
  139. Quick demo

  140. Selenium IDE

  141. Record tests in your browser (with Firefox)

  142. Not the most elegant UI

  143. None
  144. All the normal assertion goodies of any other testing framework

  145. Save and distribute test suites

  146. Click record and start testing

  147. None
  148. Thanks, that's all I've got! ๏ http://kuzemchak.net/ ๏ https://github.com/akuzemchak ๏

    @akuzemchak