{"action":"created","comment":{"url":"https://api.github.com/repos/18F/micropurchase/pulls/comments/60098821","id":60098821,"diff_hunk":"@@ -0,0 +1,272 @@\n+# How We Are Trying To Limit Technical Debt\n+\n+Rails is a great framework for getting started quickly. You just need\n+to generate a few models and off you go! The problem is that this\n+invariably leads to unused and undermaintained code accumulating in\n+different places like your models, views, and controllers all over\n+your project. We have all learned a few basic mantras like \"fat\n+models, thin controllers\" to combat some of this cruft, but it still\n+leads to your models acquiring hundreds of methods and your views as\n+branching nests of conditionals. We're trying a few techniques on this\n+project to forestall that that could be summarized with the following principles:\n+\n+1. Long model classes are bad\n+2. NullObject is better than nil\n+3. View logic should not be in the templates\n+\n+This document will explore how we try to adhere to these principles in\n+how we organize the project. We don't usually design with these\n+approaches up front -- it's often more effective to refactor after the\n+fact -- and there are definitely areas where code could be improved\n+still. But it's a continuous process we are learning from and\n+implementing when it's time to reengineer certain components. We are\n+indebted to Kane Baccigalupi for all her insight and experience with\n+this approach.\n+\n+# Presenters\n+\n+Long classes are often flagged as a code smell because it's hard to\n+keep track of all the methods within them and easy to keep around\n+methods that a developer built in case someone needed it (nobody did)\n+or worse still to reimplement another method under a new name\n+(display_price, formatted_price, page_price). Similarly, long classes\n+are often trying to fulfill several distinct purposes at once, which\n+compromises their conceptual clarity and makes it confusing for new\n+developers to understand how they work.\n+\n+We tackle that by using Plain Old Ruby Objects (POROs) to wrap our\n+basic classes in additional functionality when we need it. This is\n+called the Decorator pattern if you are used to design patterns, but\n+we tend to give these classes different names based on their\n+functionality. Here is a\n+[good primer on various ways in which this approach can clean up Rails projects](http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/)\n+if you wanted a bit more background, but this is how we apply it to\n+this project.\n+\n+If you don't now what a Decorator is, it's an object that wraps\n+another object inside of it and can delegate some methods to the\n+internal object directly and define new methods of its own.\n+\n+I'm going to look at the Auction model and the various classes around\n+it as an example of how we are applying this approach:\n+\n+1. [Auction](app/models/auction.rb): this is the ActiveRecord class","path":"TECHNICAL_DEBT.md","position":53,"original_position":53,"commit_id":"aa49cfac170ceb485d527965b4703657d8af81b0","original_commit_id":"aa49cfac170ceb485d527965b4703657d8af81b0","user":{"login":"jessieay","id":601515,"avatar_url":"https://avatars.githubusercontent.com/u/601515?v=3","gravatar_id":"","url":"https://api.github.com/users/jessieay","html_url":"https://github.com/jessieay","followers_url":"https://api.github.com/users/jessieay/followers","following_url":"https://api.github.com/users/jessieay/following{/other_user}","gists_url":"https://api.github.com/users/jessieay/gists{/gist_id}","starred_url":"https://api.github.com/users/jessieay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jessieay/subscriptions","organizations_url":"https://api.github.com/users/jessieay/orgs","repos_url":"https://api.github.com/users/jessieay/repos","events_url":"https://api.github.com/users/jessieay/events{/privacy}","received_events_url":"https://api.github.com/users/jessieay/received_events","type":"User","site_admin":false},"body":"cool that this link works! ","created_at":"2016-04-18T17:26:19Z","updated_at":"2016-04-18T17:26:19Z","html_url":"https://github.com/18F/micropurchase/pull/420#discussion_r60098821","pull_request_url":"https://api.github.com/repos/18F/micropurchase/pulls/420","_links":{"self":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/comments/60098821"},"html":{"href":"https://github.com/18F/micropurchase/pull/420#discussion_r60098821"},"pull_request":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/420"}}},"pull_request":{"url":"https://api.github.com/repos/18F/micropurchase/pulls/420","id":66659712,"html_url":"https://github.com/18F/micropurchase/pull/420","diff_url":"https://github.com/18F/micropurchase/pull/420.diff","patch_url":"https://github.com/18F/micropurchase/pull/420.patch","issue_url":"https://api.github.com/repos/18F/micropurchase/issues/420","number":420,"state":"open","locked":false,"title":"Document the techniques we are using to deter technical debt","user":{"login":"harrisj","id":2044,"avatar_url":"https://avatars.githubusercontent.com/u/2044?v=3","gravatar_id":"","url":"https://api.github.com/users/harrisj","html_url":"https://github.com/harrisj","followers_url":"https://api.github.com/users/harrisj/followers","following_url":"https://api.github.com/users/harrisj/following{/other_user}","gists_url":"https://api.github.com/users/harrisj/gists{/gist_id}","starred_url":"https://api.github.com/users/harrisj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/harrisj/subscriptions","organizations_url":"https://api.github.com/users/harrisj/orgs","repos_url":"https://api.github.com/users/harrisj/repos","events_url":"https://api.github.com/users/harrisj/events{/privacy}","received_events_url":"https://api.github.com/users/harrisj/received_events","type":"User","site_admin":false},"body":"This should be ready to go. Please merge with a squashed commit.","created_at":"2016-04-15T15:20:08Z","updated_at":"2016-04-18T17:26:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"001c21a69f1959d1a08e1b255b5c8a2de32994a5","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/18F/micropurchase/pulls/420/commits","review_comments_url":"https://api.github.com/repos/18F/micropurchase/pulls/420/comments","review_comment_url":"https://api.github.com/repos/18F/micropurchase/pulls/comments{/number}","comments_url":"https://api.github.com/repos/18F/micropurchase/issues/420/comments","statuses_url":"https://api.github.com/repos/18F/micropurchase/statuses/aa49cfac170ceb485d527965b4703657d8af81b0","head":{"label":"18F:technical-debt-doc","ref":"technical-debt-doc","sha":"aa49cfac170ceb485d527965b4703657d8af81b0","user":{"login":"18F","id":6233994,"avatar_url":"https://avatars.githubusercontent.com/u/6233994?v=3","gravatar_id":"","url":"https://api.github.com/users/18F","html_url":"https://github.com/18F","followers_url":"https://api.github.com/users/18F/followers","following_url":"https://api.github.com/users/18F/following{/other_user}","gists_url":"https://api.github.com/users/18F/gists{/gist_id}","starred_url":"https://api.github.com/users/18F/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/18F/subscriptions","organizations_url":"https://api.github.com/users/18F/orgs","repos_url":"https://api.github.com/users/18F/repos","events_url":"https://api.github.com/users/18F/events{/privacy}","received_events_url":"https://api.github.com/users/18F/received_events","type":"Organization","site_admin":false},"repo":{"id":43830328,"name":"micropurchase","full_name":"18F/micropurchase","owner":{"login":"18F","id":6233994,"avatar_url":"https://avatars.githubusercontent.com/u/6233994?v=3","gravatar_id":"","url":"https://api.github.com/users/18F","html_url":"https://github.com/18F","followers_url":"https://api.github.com/users/18F/followers","following_url":"https://api.github.com/users/18F/following{/other_user}","gists_url":"https://api.github.com/users/18F/gists{/gist_id}","starred_url":"https://api.github.com/users/18F/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/18F/subscriptions","organizations_url":"https://api.github.com/users/18F/orgs","repos_url":"https://api.github.com/users/18F/repos","events_url":"https://api.github.com/users/18F/events{/privacy}","received_events_url":"https://api.github.com/users/18F/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/18F/micropurchase","description":"18F's micro-purchase threshold experiment management app.","fork":false,"url":"https://api.github.com/repos/18F/micropurchase","forks_url":"https://api.github.com/repos/18F/micropurchase/forks","keys_url":"https://api.github.com/repos/18F/micropurchase/keys{/key_id}","collaborators_url":"https://api.github.com/repos/18F/micropurchase/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/18F/micropurchase/teams","hooks_url":"https://api.github.com/repos/18F/micropurchase/hooks","issue_events_url":"https://api.github.com/repos/18F/micropurchase/issues/events{/number}","events_url":"https://api.github.com/repos/18F/micropurchase/events","assignees_url":"https://api.github.com/repos/18F/micropurchase/assignees{/user}","branches_url":"https://api.github.com/repos/18F/micropurchase/branches{/branch}","tags_url":"https://api.github.com/repos/18F/micropurchase/tags","blobs_url":"https://api.github.com/repos/18F/micropurchase/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/18F/micropurchase/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/18F/micropurchase/git/refs{/sha}","trees_url":"https://api.github.com/repos/18F/micropurchase/git/trees{/sha}","statuses_url":"https://api.github.com/repos/18F/micropurchase/statuses/{sha}","languages_url":"https://api.github.com/repos/18F/micropurchase/languages","stargazers_url":"https://api.github.com/repos/18F/micropurchase/stargazers","contributors_url":"https://api.github.com/repos/18F/micropurchase/contributors","subscribers_url":"https://api.github.com/repos/18F/micropurchase/subscribers","subscription_url":"https://api.github.com/repos/18F/micropurchase/subscription","commits_url":"https://api.github.com/repos/18F/micropurchase/commits{/sha}","git_commits_url":"https://api.github.com/repos/18F/micropurchase/git/commits{/sha}","comments_url":"https://api.github.com/repos/18F/micropurchase/comments{/number}","issue_comment_url":"https://api.github.com/repos/18F/micropurchase/issues/comments{/number}","contents_url":"https://api.github.com/repos/18F/micropurchase/contents/{+path}","compare_url":"https://api.github.com/repos/18F/micropurchase/compare/{base}...{head}","merges_url":"https://api.github.com/repos/18F/micropurchase/merges","archive_url":"https://api.github.com/repos/18F/micropurchase/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/18F/micropurchase/downloads","issues_url":"https://api.github.com/repos/18F/micropurchase/issues{/number}","pulls_url":"https://api.github.com/repos/18F/micropurchase/pulls{/number}","milestones_url":"https://api.github.com/repos/18F/micropurchase/milestones{/number}","notifications_url":"https://api.github.com/repos/18F/micropurchase/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/18F/micropurchase/labels{/name}","releases_url":"https://api.github.com/repos/18F/micropurchase/releases{/id}","deployments_url":"https://api.github.com/repos/18F/micropurchase/deployments","created_at":"2015-10-07T16:47:29Z","updated_at":"2016-04-14T15:12:36Z","pushed_at":"2016-04-18T02:02:59Z","git_url":"git://github.com/18F/micropurchase.git","ssh_url":"git@github.com:18F/micropurchase.git","clone_url":"https://github.com/18F/micropurchase.git","svn_url":"https://github.com/18F/micropurchase","homepage":"https://micropurchase.18f.gov","size":3485,"stargazers_count":24,"watchers_count":24,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":14,"mirror_url":null,"open_issues_count":69,"forks":14,"open_issues":69,"watchers":24,"default_branch":"develop"}},"base":{"label":"18F:develop","ref":"develop","sha":"b1f834c5108a099cc8038a30737cd5f6950b9362","user":{"login":"18F","id":6233994,"avatar_url":"https://avatars.githubusercontent.com/u/6233994?v=3","gravatar_id":"","url":"https://api.github.com/users/18F","html_url":"https://github.com/18F","followers_url":"https://api.github.com/users/18F/followers","following_url":"https://api.github.com/users/18F/following{/other_user}","gists_url":"https://api.github.com/users/18F/gists{/gist_id}","starred_url":"https://api.github.com/users/18F/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/18F/subscriptions","organizations_url":"https://api.github.com/users/18F/orgs","repos_url":"https://api.github.com/users/18F/repos","events_url":"https://api.github.com/users/18F/events{/privacy}","received_events_url":"https://api.github.com/users/18F/received_events","type":"Organization","site_admin":false},"repo":{"id":43830328,"name":"micropurchase","full_name":"18F/micropurchase","owner":{"login":"18F","id":6233994,"avatar_url":"https://avatars.githubusercontent.com/u/6233994?v=3","gravatar_id":"","url":"https://api.github.com/users/18F","html_url":"https://github.com/18F","followers_url":"https://api.github.com/users/18F/followers","following_url":"https://api.github.com/users/18F/following{/other_user}","gists_url":"https://api.github.com/users/18F/gists{/gist_id}","starred_url":"https://api.github.com/users/18F/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/18F/subscriptions","organizations_url":"https://api.github.com/users/18F/orgs","repos_url":"https://api.github.com/users/18F/repos","events_url":"https://api.github.com/users/18F/events{/privacy}","received_events_url":"https://api.github.com/users/18F/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/18F/micropurchase","description":"18F's micro-purchase threshold experiment management app.","fork":false,"url":"https://api.github.com/repos/18F/micropurchase","forks_url":"https://api.github.com/repos/18F/micropurchase/forks","keys_url":"https://api.github.com/repos/18F/micropurchase/keys{/key_id}","collaborators_url":"https://api.github.com/repos/18F/micropurchase/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/18F/micropurchase/teams","hooks_url":"https://api.github.com/repos/18F/micropurchase/hooks","issue_events_url":"https://api.github.com/repos/18F/micropurchase/issues/events{/number}","events_url":"https://api.github.com/repos/18F/micropurchase/events","assignees_url":"https://api.github.com/repos/18F/micropurchase/assignees{/user}","branches_url":"https://api.github.com/repos/18F/micropurchase/branches{/branch}","tags_url":"https://api.github.com/repos/18F/micropurchase/tags","blobs_url":"https://api.github.com/repos/18F/micropurchase/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/18F/micropurchase/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/18F/micropurchase/git/refs{/sha}","trees_url":"https://api.github.com/repos/18F/micropurchase/git/trees{/sha}","statuses_url":"https://api.github.com/repos/18F/micropurchase/statuses/{sha}","languages_url":"https://api.github.com/repos/18F/micropurchase/languages","stargazers_url":"https://api.github.com/repos/18F/micropurchase/stargazers","contributors_url":"https://api.github.com/repos/18F/micropurchase/contributors","subscribers_url":"https://api.github.com/repos/18F/micropurchase/subscribers","subscription_url":"https://api.github.com/repos/18F/micropurchase/subscription","commits_url":"https://api.github.com/repos/18F/micropurchase/commits{/sha}","git_commits_url":"https://api.github.com/repos/18F/micropurchase/git/commits{/sha}","comments_url":"https://api.github.com/repos/18F/micropurchase/comments{/number}","issue_comment_url":"https://api.github.com/repos/18F/micropurchase/issues/comments{/number}","contents_url":"https://api.github.com/repos/18F/micropurchase/contents/{+path}","compare_url":"https://api.github.com/repos/18F/micropurchase/compare/{base}...{head}","merges_url":"https://api.github.com/repos/18F/micropurchase/merges","archive_url":"https://api.github.com/repos/18F/micropurchase/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/18F/micropurchase/downloads","issues_url":"https://api.github.com/repos/18F/micropurchase/issues{/number}","pulls_url":"https://api.github.com/repos/18F/micropurchase/pulls{/number}","milestones_url":"https://api.github.com/repos/18F/micropurchase/milestones{/number}","notifications_url":"https://api.github.com/repos/18F/micropurchase/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/18F/micropurchase/labels{/name}","releases_url":"https://api.github.com/repos/18F/micropurchase/releases{/id}","deployments_url":"https://api.github.com/repos/18F/micropurchase/deployments","created_at":"2015-10-07T16:47:29Z","updated_at":"2016-04-14T15:12:36Z","pushed_at":"2016-04-18T02:02:59Z","git_url":"git://github.com/18F/micropurchase.git","ssh_url":"git@github.com:18F/micropurchase.git","clone_url":"https://github.com/18F/micropurchase.git","svn_url":"https://github.com/18F/micropurchase","homepage":"https://micropurchase.18f.gov","size":3485,"stargazers_count":24,"watchers_count":24,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":14,"mirror_url":null,"open_issues_count":69,"forks":14,"open_issues":69,"watchers":24,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/420"},"html":{"href":"https://github.com/18F/micropurchase/pull/420"},"issue":{"href":"https://api.github.com/repos/18F/micropurchase/issues/420"},"comments":{"href":"https://api.github.com/repos/18F/micropurchase/issues/420/comments"},"review_comments":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/420/comments"},"review_comment":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/18F/micropurchase/pulls/420/commits"},"statuses":{"href":"https://api.github.com/repos/18F/micropurchase/statuses/aa49cfac170ceb485d527965b4703657d8af81b0"}}}}