Connection ϟ Required 2017-06-30T12:33:06-04:00 / Zach Moazeni zach.moazeni@gmail.com Improving Report Performance 2017-06-06T10:00:00-04:00 /blog/2017/06/improving-report-performance Zach Moazeni zach.moazeni@gmail.com <p><em>Originally posted to <a href="http://techtime.getharvest.com/blog/improving-report-performance">http://techtime.getharvest.com/blog/improving-report-performance</a>.</em></p> <p>Last year, I <a href="https://www.getharvest.com/blog/2016/12/a-gift-of-time/">made a promise</a> to share the gritty details of how we improved our reporting performance dramatically, anywhere from a factor of 4x to 10x. I spent over a year on this project and I am very pleased with the results. I want to share some of our wins with you all in case they help anyone else.</p> <p>First, you have to know a little about our data model for these gains to make sense. Our customers have users in their account track time for tasks on projects they’re assigned to.</p> <svg version="1.2" viewBox="0 0 17780 12700" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve"> <defs class="ClipPathGroup"> <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"> <rect x="0" y="0" width="17780" height="12700" /> </clipPath> </defs> <defs class="TextShapeIndex"> <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15" /> </defs> <defs class="EmbeddedBulletChars"> <g id="bullet-char-template(57356)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z" /> </g> <g id="bullet-char-template(57354)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z" /> </g> <g id="bullet-char-template(10146)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z" /> </g> <g id="bullet-char-template(10132)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z" /> </g> <g id="bullet-char-template(10007)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z" /> </g> <g id="bullet-char-template(10004)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z" /> </g> <g id="bullet-char-template(9679)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z" /> </g> <g id="bullet-char-template(8226)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z" /> </g> <g id="bullet-char-template(8211)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z" /> </g> </defs> <defs class="TextEmbeddedBitmaps" /> <g> <g id="id2" class="Master_Slide"> <g id="bg-id2" class="Background" /> <g id="bo-id2" class="BackgroundObjects" /> </g> </g> <g class="SlideGroup"> <g> <g id="id1" class="Slide" clip-path="url(#presentation_clip_path)"> <g class="Page"> <g class="com.sun.star.drawing.CustomShape"> <g id="id3"> <rect class="BoundingBox" stroke="none" fill="none" x="6349" y="7619" width="5083" height="5083" /> <path fill="rgb(255,99,71)" stroke="none" d="M 8890,12700 L 6350,12700 6350,7620 11430,7620 11430,12700 8890,12700 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 8890,12700 L 6350,12700 6350,7620 11430,7620 11430,12700 8890,12700 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="6854" y="10406"><tspan fill="rgb(0,0,0)" stroke="none">Time Entry</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id4"> <rect class="BoundingBox" stroke="none" fill="none" x="-1" y="-1" width="5083" height="5083" /> <path fill="rgb(60,179,113)" stroke="none" d="M 2540,5080 L 0,5080 0,0 5080,0 5080,5080 2540,5080 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 2540,5080 L 0,5080 0,0 5080,0 5080,5080 2540,5080 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="1679" y="2786"><tspan fill="rgb(0,0,0)" stroke="none">User</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id5"> <rect class="BoundingBox" stroke="none" fill="none" x="12699" y="-1" width="5083" height="5083" /> <path fill="rgb(192,192,192)" stroke="none" d="M 15240,5080 L 12700,5080 12700,0 17780,0 17780,5080 15240,5080 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 15240,5080 L 12700,5080 12700,0 17780,0 17780,5080 15240,5080 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="14383" y="2786"><tspan fill="rgb(0,0,0)" stroke="none">Task</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id6"> <rect class="BoundingBox" stroke="none" fill="none" x="6349" y="-1" width="5083" height="5083" /> <path fill="rgb(255,255,102)" stroke="none" d="M 8890,5080 L 6350,5080 6350,0 11430,0 11430,5080 8890,5080 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 8890,5080 L 6350,5080 6350,0 11430,0 11430,5080 8890,5080 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="7582" y="2786"><tspan fill="rgb(0,0,0)" stroke="none">Project</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id7"> <rect class="BoundingBox" stroke="none" fill="none" x="11430" y="5079" width="3812" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 15240,5080 L 11788,7381" /> <path fill="rgb(0,0,0)" stroke="none" d="M 11430,7620 L 11888,7495 11721,7246 11430,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id8"> <rect class="BoundingBox" stroke="none" fill="none" x="11430" y="7547" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="11680" y="8256"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id9"> <rect class="BoundingBox" stroke="none" fill="none" x="15182" y="5080" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="15432" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id10"> <rect class="BoundingBox" stroke="none" fill="none" x="8613" y="5079" width="301" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 8763,5080 L 8763,7190" /> <path fill="rgb(0,0,0)" stroke="none" d="M 8763,7620 L 8913,7170 8613,7170 8763,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id11"> <rect class="BoundingBox" stroke="none" fill="none" x="1720" y="5080" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="1970" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id12"> <rect class="BoundingBox" stroke="none" fill="none" x="8832" y="5083" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="9082" y="5792"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id13"> <rect class="BoundingBox" stroke="none" fill="none" x="8799" y="6604" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="9049" y="7313"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id14"> <rect class="BoundingBox" stroke="none" fill="none" x="2539" y="5079" width="3812" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 2540,5080 L 5992,7381" /> <path fill="rgb(0,0,0)" stroke="none" d="M 6350,7620 L 6059,7246 5892,7495 6350,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id15"> <rect class="BoundingBox" stroke="none" fill="none" x="5461" y="7547" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="5711" y="8256"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> </g> </g> </g> </g> </svg> <p>We use Percona MySQL in production. We have verified that all of our indexes are appropriate, and our queries are using the right indexes. So we knew that in order to get better performance out of these queries we needed to make some architectural changes. I spent around a month experimenting with a few different approaches on hardware similar to production using the production dataset. After trial and error, and looking at the resulting numbers, I finally had a plan.</p> <h2 id="improvement-1-organize-time-entries-table-by-project">Improvement 1: Organize time entries table by project</h2> <p>My first change may sound strange to Rails developers, but made a lot of sense at the MySQL layer. I reorganized the data on disk by changing the primary key of our time entries table. A table that holds over 500 million rows. This is not a MySQL-specific concept. If you’re interested you can also search for “clustered index”, but for MySQL the primary key is the clustered index.</p> <p><a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-index-types.html">MySQL’s docs</a> explain it better than I can:</p> <blockquote> <p>Accessing a row through the clustered index is fast because the index search leads directly to the page with all the row data. If a table is large, the clustered index architecture often saves a disk I/O operation when compared to storage organizations that store row data using a different page from the index record.</p> </blockquote> <p><strong>Making this change sped up our report queries instantly by about 50% across the board (queries that took 12 seconds now took around 6 seconds).</strong></p> <p>MySQL stores data in pages, or clumps of rows/records. By default, for typical tables with an auto-incremented id, that means related rows are scattered around the disk based on the order they were inserted. Most of our queries retrieve and report on all time entries for a given project, so when I changed the primary key from <code class="highlighter-rouge">(id)</code> to an existing index which is <code class="highlighter-rouge">(project_id, spent_at, id)</code>, it meant that on disk all time entries for a given project were sitting next to each other. Reducing a lot of random access.</p> <p>To help illustrate the point. Let’s assume I have a page of rows organized by the <code class="highlighter-rouge">id</code> column. It might look something like this.</p> <table> <colgroup> <col width="25%" /> <col width="25%" /> <col width="50%" /> </colgroup> <thead> <tr class="header"> <th>id</th> <th>project_id</th> <th>notes</th> </tr> </thead> <tbody> <tr> <td>1000</td> <td>50</td> <td>Reviewed Web Design</td> </tr> <tr> <td>1001</td> <td>203</td> <td>Spoke with client</td> </tr> <tr> <td>1002</td> <td>50</td> <td>Worked on new logo</td> </tr> <tr> <td>1003</td> <td>203</td> <td>Wrote down meeting notes</td> </tr> </tbody> </table> <p>As you can see, two entirely separate projects are intertwined together based on the id order. However once I change the primary key to be based on the project id, we get something like this:</p> <table> <colgroup> <col width="25%" /> <col width="25%" /> <col width="50%" /> </colgroup> <thead> <tr class="header"> <th>id</th> <th>project_id</th> <th>notes</th> </tr> </thead> <tbody> <tr> <td>1000</td> <td>50</td> <td>Reviewed Web Design</td> </tr> <tr> <td>1002</td> <td>50</td> <td>Worked on new logo</td> </tr> <tr> <td>2000</td> <td>50</td> <td>Helped web designer with CSS</td> </tr> <tr> <td>5000</td> <td>50</td> <td>Final call with client</td> </tr> </tbody> </table> <p>All time entries for that project are now grouped together within the pages on disk.</p> <p>Now I was really skeptical that this would have any performance boost because our monster database servers are using high performance enterprise SSDs in a RAID 10. On top of that, we have an extremely large InnoDB Buffer Pool with a hit rate of around 99%. So most data is served from memory, and even if it does have to hit the disk, it’s still much faster than than a traditional spinning platter. But our metrics proved me wrong and we were able to take an easy win right away.</p> <p>To keep Rails happy, we still have the auto-incremented id, but I added a manual unique index on it and I added <code class="highlighter-rouge">self.primary_key = :id</code> to the top of our time entry model. But for all intents and purposes this change is transparent to Rails and the rest of our code.</p> <p>This technique is not a free lunch. This will create more data pages overall for the table because MySQL will want to keep a portion of the page empty as defined by the <a href="https://dev.mysql.com/doc/refman/5.7/en/index-page-merge-threshold.html"><code class="highlighter-rouge">MERGE_THRESHOLD</code></a>. We are effectively trading disk space for much better read performance, and in our scenario that was perfectly acceptable. The disk space required for the time entries table ballooned after this change (and the next one I’ll talk about). If you are interested in more information about MySQL’s page mechanics, take a look at <a href="https://www.percona.com/blog/2017/04/10/innodb-page-merging-and-page-splitting/">this great post on Percona’s blog</a>.</p> <h2 id="improvement-2-denormalize-important-reporting-data-into-time-entries">Improvement 2: Denormalize important reporting data into time entries</h2> <p>The next step to increasing performance was to denormalize the important reporting data into the time entries table. This is not a new practice at all, but it did have some shocking results for our data set.</p> <p>One thing to know is that Harvest also allows a lot of flexibility in the way a project can be configured. Those settings will determine what time is billable and by how much. Meaning that some combinations of project settings store important report information in the join tables. For example, a project may bill by a person’s hourly rate. So for our report queries we also have to join those tables in order to have all the key pieces of information available. As you can imagine our reporting queries end up requiring a lot of boilerplate for our joins.</p> <svg version="1.2" viewBox="0 0 17780 20320" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve"> <defs class="ClipPathGroup"> <clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse"> <rect x="0" y="0" width="17780" height="20320" /> </clipPath> </defs> <defs class="TextShapeIndex"> <g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18 id19 id20 id21 id22 id23 id24 id25 id26 id27 id28 id29 id30 id31 id32 id33" /> </defs> <defs class="EmbeddedBulletChars"> <g id="bullet-char-template(57356)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z" /> </g> <g id="bullet-char-template(57354)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z" /> </g> <g id="bullet-char-template(10146)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z" /> </g> <g id="bullet-char-template(10132)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z" /> </g> <g id="bullet-char-template(10007)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z" /> </g> <g id="bullet-char-template(10004)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z" /> </g> <g id="bullet-char-template(9679)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z" /> </g> <g id="bullet-char-template(8226)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z" /> </g> <g id="bullet-char-template(8211)" transform="scale(0.00048828125,-0.00048828125)"> <path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z" /> </g> </defs> <defs class="TextEmbeddedBitmaps" /> <g> <g id="id2" class="Master_Slide"> <g id="bg-id2" class="Background" /> <g id="bo-id2" class="BackgroundObjects" /> </g> </g> <g class="SlideGroup"> <g> <g id="id1" class="Slide" clip-path="url(#presentation_clip_path)"> <g class="Page"> <g class="com.sun.star.drawing.CustomShape"> <g id="id3"> <rect class="BoundingBox" stroke="none" fill="none" x="6349" y="15239" width="5083" height="5083" /> <path fill="rgb(255,99,71)" stroke="none" d="M 8890,20320 L 6350,20320 6350,15240 11430,15240 11430,20320 8890,20320 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 8890,20320 L 6350,20320 6350,15240 11430,15240 11430,20320 8890,20320 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="6854" y="18026"><tspan fill="rgb(0,0,0)" stroke="none">Time Entry</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id4"> <rect class="BoundingBox" stroke="none" fill="none" x="-1" y="7619" width="5083" height="5083" /> <path fill="rgb(60,179,113)" stroke="none" d="M 2540,12700 L 0,12700 0,7620 5080,7620 5080,12700 2540,12700 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 2540,12700 L 0,12700 0,7620 5080,7620 5080,12700 2540,12700 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="1679" y="10406"><tspan fill="rgb(0,0,0)" stroke="none">User</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id5"> <rect class="BoundingBox" stroke="none" fill="none" x="12699" y="7619" width="5083" height="5083" /> <path fill="rgb(192,192,192)" stroke="none" d="M 15240,12700 L 12700,12700 12700,7620 17780,7620 17780,12700 15240,12700 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 15240,12700 L 12700,12700 12700,7620 17780,7620 17780,12700 15240,12700 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="14383" y="10406"><tspan fill="rgb(0,0,0)" stroke="none">Task</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id6"> <rect class="BoundingBox" stroke="none" fill="none" x="6349" y="7619" width="5083" height="5083" /> <path fill="rgb(255,255,102)" stroke="none" d="M 8890,12700 L 6350,12700 6350,7620 11430,7620 11430,12700 8890,12700 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 8890,12700 L 6350,12700 6350,7620 11430,7620 11430,12700 8890,12700 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="7582" y="10406"><tspan fill="rgb(0,0,0)" stroke="none">Project</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id7"> <rect class="BoundingBox" stroke="none" fill="none" x="11430" y="12699" width="3812" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 15240,12700 L 11788,15001" /> <path fill="rgb(0,0,0)" stroke="none" d="M 11430,15240 L 11888,15115 11721,14866 11430,15240 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id8"> <rect class="BoundingBox" stroke="none" fill="none" x="11430" y="15167" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="11680" y="15876"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id9"> <rect class="BoundingBox" stroke="none" fill="none" x="15182" y="12700" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="15432" y="13409"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id10"> <rect class="BoundingBox" stroke="none" fill="none" x="8613" y="12699" width="301" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 8763,12700 L 8763,14810" /> <path fill="rgb(0,0,0)" stroke="none" d="M 8763,15240 L 8913,14790 8613,14790 8763,15240 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id11"> <rect class="BoundingBox" stroke="none" fill="none" x="1720" y="12700" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="1970" y="13409"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id12"> <rect class="BoundingBox" stroke="none" fill="none" x="8832" y="12703" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="9082" y="13412"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id13"> <rect class="BoundingBox" stroke="none" fill="none" x="8799" y="14224" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="9049" y="14933"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id14"> <rect class="BoundingBox" stroke="none" fill="none" x="2539" y="12699" width="3812" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 2540,12700 L 5992,15001" /> <path fill="rgb(0,0,0)" stroke="none" d="M 6350,15240 L 6059,14866 5892,15115 6350,15240 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id15"> <rect class="BoundingBox" stroke="none" fill="none" x="5461" y="15167" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="5711" y="15876"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id16"> <rect class="BoundingBox" stroke="none" fill="none" x="3174" y="-1" width="5083" height="5083" /> <path fill="rgb(173,255,47)" stroke="none" d="M 5715,5080 L 3175,5080 3175,0 8255,0 8255,5080 5715,5080 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 5715,5080 L 3175,5080 3175,0 8255,0 8255,5080 5715,5080 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="4765" y="2383"><tspan fill="rgb(0,0,0)" stroke="none">User </tspan></tspan></tspan><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="3567" y="3188"><tspan fill="rgb(0,0,0)" stroke="none">Assignment</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id17"> <rect class="BoundingBox" stroke="none" fill="none" x="3556" y="5079" width="1526" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 5080,5080 L 3777,7251" /> <path fill="rgb(0,0,0)" stroke="none" d="M 3556,7620 L 3916,7311 3659,7157 3556,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id18"> <rect class="BoundingBox" stroke="none" fill="none" x="6349" y="5079" width="1399" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 6350,5080 L 7540,7243" /> <path fill="rgb(0,0,0)" stroke="none" d="M 7747,7620 L 7662,7153 7399,7298 7747,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.CustomShape"> <g id="id19"> <rect class="BoundingBox" stroke="none" fill="none" x="9524" y="-1" width="5083" height="5083" /> <path fill="rgb(255,235,205)" stroke="none" d="M 12065,5080 L 9525,5080 9525,0 14605,0 14605,5080 12065,5080 Z" /> <path fill="none" stroke="rgb(52,101,164)" d="M 12065,5080 L 9525,5080 9525,0 14605,0 14605,5080 12065,5080 Z" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="11119" y="2383"><tspan fill="rgb(0,0,0)" stroke="none">Task </tspan></tspan></tspan><tspan class="TextParagraph" font-family="Georgia, serif" font-size="706px" font-weight="700"><tspan class="TextPosition" x="9917" y="3188"><tspan fill="rgb(0,0,0)" stroke="none">Assignment</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id20"> <rect class="BoundingBox" stroke="none" fill="none" x="10160" y="5079" width="1272" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 11430,5080 L 10352,7235" /> <path fill="rgb(0,0,0)" stroke="none" d="M 10160,7620 L 10495,7285 10227,7150 10160,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.LineShape"> <g id="id21"> <rect class="BoundingBox" stroke="none" fill="none" x="12699" y="5079" width="1399" height="2542" /> <path fill="none" stroke="rgb(0,0,0)" d="M 12700,5080 L 13890,7243" /> <path fill="rgb(0,0,0)" stroke="none" d="M 14097,7620 L 14012,7153 13749,7298 14097,7620 Z" /> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id22"> <rect class="BoundingBox" stroke="none" fill="none" x="2794" y="6604" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="3044" y="7313"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id23"> <rect class="BoundingBox" stroke="none" fill="none" x="3820" y="5080" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="4070" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id24"> <rect class="BoundingBox" stroke="none" fill="none" x="7747" y="6645" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="7997" y="7354"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id25"> <rect class="BoundingBox" stroke="none" fill="none" x="6868" y="5080" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="7118" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id26"> <rect class="BoundingBox" stroke="none" fill="none" x="2795" y="6605" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="3045" y="7314"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id27"> <rect class="BoundingBox" stroke="none" fill="none" x="3821" y="5081" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="4071" y="5790"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id28"> <rect class="BoundingBox" stroke="none" fill="none" x="14087" y="6645" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="14337" y="7354"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id29"> <rect class="BoundingBox" stroke="none" fill="none" x="13208" y="5080" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="13458" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id30"> <rect class="BoundingBox" stroke="none" fill="none" x="7747" y="6645" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="7997" y="7354"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id31"> <rect class="BoundingBox" stroke="none" fill="none" x="6868" y="5080" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="7118" y="5789"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id32"> <rect class="BoundingBox" stroke="none" fill="none" x="9398" y="6645" width="774" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="9648" y="7354"><tspan fill="rgb(0,0,0)" stroke="none">1</tspan></tspan></tspan></text> </g> </g> <g class="com.sun.star.drawing.TextShape"> <g id="id33"> <rect class="BoundingBox" stroke="none" fill="none" x="10160" y="5121" width="880" height="976" /> <text class="TextShape"><tspan class="TextParagraph" font-family="Georgia, serif" font-size="635px" font-weight="400"><tspan class="TextPosition" x="10410" y="5830"><tspan fill="rgb(0,0,0)" stroke="none">n</tspan></tspan></tspan></text> </g> </g> </g> </g> </g> </g> </svg> <p>If you have used our public API, these concepts will be rather straight forward.</p> <p>If you look back at that graph, you can imagine how our queries reporting on time entries would have to continue to join both the user and task assignments tables. Just looking at “what is the billable rate for this time entry” could end up looking at either the project, user assignment, or task assignment based on how the project is setup to be billed (billed by project, user, or task). Even asking “is this time entry billable?” will always consult the task assignments table (a task is marked billable or non-billable for a given project).</p> <p>That means in order to show anything meaningful for our customers, we have to join these tables all the time. The nice thing about that design is that if a value changes in one place, all reports are instantly updated.</p> <p>However, what I didn’t realize was the cost involved in joining those tables. As I said previously, our indexes are set up just fine. However even with great indexes, when you take millions of time entry rows and join them to hundreds of related task assignment rows, there is still lots of work for MySQL to process. All things considered, it is rather impressive how quickly it can process that much work. But even after I reorganized the time entries table, I wanted better performance than our 6 second worst case query.</p> <p>So in addition to changing the primary key, I also added a few more columns on the time entries table, like <code class="highlighter-rouge">billable</code> and <code class="highlighter-rouge">billable_rate</code>. I also updated our Rails code to populate those fields when a time entry was created or updated, and <strong>also</strong> when a project’s settings changed dramatically.</p> <p>This section of denormalization code is complex. However, the upshot is that now all of our reporting queries have immediate access to the necessary data at the time entries level. We no longer need to join any other tables in order to calculate reporting data, meaning our reporting queries become easier to understand and maintain and less error prone. It also means that the bulk of the complexity is isolated to one section that the rest of the application depends on.</p> <p><strong>Shockingly, that took our worst case 6 second query down to around 1.5 seconds by just eliminating those joins.</strong></p> <p>To step back, with these two changes, our worst case 12 second query was now around 1.5 seconds. Decent sized projects that originally took around 2 seconds now ran in 0.25 seconds. Both of these changes contributed to this massive performance boost.</p> <p>Just like changing the primary key, this strategy isn’t a free lunch either. Now that the reporting data is denormalized, there’s always a possibility for it to fall out of sync with the original data stored in separate tables. Scenarios like programmer errors, customer support changes, or even freakish once-in-a-blue-moon events all have the potential of messing with that balance.</p> <p>To mitigate those problems, as part of the Rails changes, we’re testing our denormalization code very heavily. We are also running a consistency check once a day to make sure all of those denormalized fields stored have correct data. These two strategies have worked remarkably well for finding problems when they occur.</p> <p>This also worked out wonderfully as we transparently rolled out these changes to our customers. Even before we started using the new data in the report queries, we could store and verify the contents, allowing me to hunt down any holes in our logic and ensure the data can be trusted. These steps have also helped us with current development as we <a href="https://www.getharvest.com/blog/2017/05/introducing-fixed-fee-projects/">explore new project settings</a>.</p> <p>Aside from the logical errors that could happen, we also take another disk space penalty as we widen our large table. With the new primary key and the new columns, our time entries table ballooned by about 25%. But for our data set, this trade off was an easy decision.</p> <h2 id="parting-thoughts">Parting thoughts</h2> <p>I am extremely happy with these results and I hope that this information can help other teams as they examine their own application’s query performance. I believe we made these changes at the right time and I would not have started out our application with either of these techniques. Going forward we’ll have to see whether these approaches still apply or if there are smarter choices we can make, but for now, we can celebrate.</p> A hypothesis about the number of assertions in a test 2014-12-12T00:00:00-05:00 /blog/2014/12/a-hypothesis-about-the-number-of-assertions-per-test Zach Moazeni zach.moazeni@gmail.com <p>Lately at work we have been discussing a testing style that has been named <a href="http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html">one assertion per test</a>. Here is <a href="http://maxheapsize.com/2011/06/14/one-assert-per-test-really/">another blog post</a> describing this style.</p> <p>In essence for one-assertion-per-test the goal is to keep all tests focused on one part of the behavior. A couple benefits of this style:</p> <ul> <li>You don’t have to re-run a test multiple times in order to make it pass. If it fails, you can red-green it without another assertion in the same test failing.</li> <li>You can give more descriptive names to the tests.</li> </ul> <p>I strongly dislike the one-assertion-per-test heuristic, but I do like my tests having a single focus. I’ve even called it “one focus per test” when discussing this with collegues.</p> <p>What is interesting about this discussion is not that one style is clearly right and one is clearly wrong (who am I kidding? “one focus per test” all the way baby!). It is that a lot of the discussion keeps circling back to <em>“It’s easier for me to follow …“</em> or <em>“It’s easier for me to grok…“</em> or <em>“I can immediately understand a test when …“</em></p> <p>After thinking about it, I have a hypothesis. I think people in the different camps focus on different pieces of code in order to understand something at a glance. Imagine someone showing you a picture of a person for 5 seconds and then taking it away.</p> <p><a href="https://www.flickr.com/photos/time-to-look/15064231818" title="2014 - Vancouver - News in Chinatown by Ted McGrath, on Flickr"><img src="https://farm4.staticflickr.com/3924/15064231818_6d291253e8_c.jpg" width="800" height="347" alt="2014 - Vancouver - News in Chinatown" /></a></p> <p><em>(<a href="https://www.flickr.com/photos/time-to-look/15064231818">picture by Ted McGrath</a> under <a href="https://creativecommons.org/licenses/by-nc-sa/2.0/">creative commons license</a>)</em></p> <p>Some observers might gravitate towards their physical appearance “what colors are they wearing”, “what does their hair look like?”. Others might focus on their face “what mood are they in?” “is their facial expression telling me anything?”. Other observers might even focus on thing behind the person “where are they at?”</p> <p>Bringing this back to tests, I put together two versions of a fancy complicated example service called <a href="https://github.com/zmoazeni/numberwang.rb/blob/master/numberwang_service_spec.rb">NumberwangService</a>. It’s too easy to get sucked into trivial examples or overcomplicate the context for a specific example so my code uses the business rules to the gameshow <a href="https://www.youtube.com/watch?v=qjOZtWZ56lc">Numberwang</a>. <em>(To be fair, most internal business rules feels like Numberwang to outsiders)</em></p> <p>My hypothesis is that one-assertion-per-test developers tend to focus first on the <strong>test descriptions</strong>: <img src="/uploads/single-assertion-numberwang.png" width="800" /></p> <p>and multi-assertions-per-test developers tend to focus first on the <strong>test code</strong>: <img src="/uploads/multi-assertion-numberwang.png" width="800" /></p> <p>I’m not saying that one-assertion developers don’t care about the test body, and multi-assertion developers don’t care about the test description. But I have a hunch that developer eyes gravitate towards points like this when trying to take in and understand multiple tests at once depending on their preferences.</p> <p>I know that my habit is to focus on the test code itself and that’s why I take time to make tests readable and understandable. Like cohesive paragraphs on a page.</p> Why I enjoy writing software at Harvest 2014-11-03T12:04:00-05:00 /blog/2014/11/why-i-enjoy-writing-software-at-harvest Zach Moazeni zach.moazeni@gmail.com <p><em>Originally posted to <a href="http://techtime.getharvest.com/blog/why-i-enjoy-writing-software-at-harvest">http://techtime.getharvest.com/blog/why-i-enjoy-writing-software-at-harvest</a>.</em></p> <p>I occasionally help out the team by interviewing new candidates. The most common question I have been asked — “What do you enjoy most about working at Harvest?” — is a great question for an interviewee to ask. There are several stock answers that I think most people reach for. You can talk about how much you like the team, the culture, the flexibility, or the customers you help. I appreciate all of these at Harvest, but there are some specific anecdotes that I think really help paint a picture of what it is like to work at Harvest.</p> <h2 id="the-codebase">The Codebase</h2> <p>I enjoy working with our codebase. That sounds shallow since the codebase is just a means to an end: providing a great experience for our customers. However working daily with Harvest’s codebase is the full-time job for myself and the team I work with. If it were painful, that’d be a negative daily experience to weigh against the other benefits. I also firmly believe that our team’s ability to help solve our customer’s needs is directly influenced by the ease at which we can build features and fix problems.</p> <p>You should know that Harvest launched in 2005 before Rails had even hit version 1.0 and we have our share of legacy code that has persisted over the years. I joined Harvest over 2 years ago and my experience pretty much amounted to enterprises, startups, and consulting. For the longest time my favorite projects were <a href="http://en.wikipedia.org/wiki/Greenfield_project">greenfield</a>, where the teams could work on the problem at hand with very little regard to legacy code. So why do I enjoy working on a large old app like Harvest? Because it is amazing and inspiring to see it transform and improve over the years.</p> <p>It is one thing to launch a project for a client, fix a few bugs, and then move on or throw a ball of hacks together in order to get profitable as soon as possible. It is another world altogether to deploy a feature set, and then continue to improve and polish it as time goes on. We do have our spurts where we will rewrite old code from scratch, and we might even grumble at the previous developer’s short-sighted design choices while we do it (which is funnier when you realize you were the previous developer). But our team recognizes that old code helped get Harvest to where it is today.</p> <p>There is a respect, a beauty, and an admiration that comes working long term on a legacy codebase alongside this team.</p> <h2 id="the-team">The Team</h2> <p>The team here at Harvest is critical for our codebase to improve. Working on a legacy codebase with an understaffed crew is definitely unfun. Have you ever accidentally deployed a bug and had to answer a flood of support tickets while you’re trying to figure out a fix and deploy it? Have you ever critically botched the infrastructure and suddenly had to scramble to fix the servers?</p> <p>I am grateful for the teams we have here at Harvest. I’m a big fan of our Harvest Experts who genuinely care about solving our customer’s problems and diagnose bug symptoms to pass on to developers. They honestly mean it when they tell our customers to write or call in when they need help.</p> <p>I am relieved we have a brilliant and hardworking Operations team that is focused on our infrastructure 24x7 dealing with daily problems ranging from security reports and patches to capacity planning.</p> <p>I am thankful for our Product Design team who thinks a lot about the customer’s interface experience. They also work closely with the Development team and adapt if technical limitations are uncovered.</p> <p>We have several other roles too, like our Mobile Developers, Quality Assurance, Marketing, External Integrations, and Office Managers that all fit in a unique way to improving our company and product. Because of these roles, I can focus on what I do best.</p> <h2 id="roles-and-communication">Roles and Communication</h2> <p>Now you might get the impression that each of these roles are rigidly defined and add to office bureaucracy; Developers need to submit “Database Change Requests” to Ops and everyone is busy filling out TPS reports. Not at all.</p> <p>Each one of these teams fluidly communicates with other teams in order to solve the problems at hand. Developers are asked to take over certain support tickets. Ops members are invited to a technical design discussion. Experts are asked about common customer problems while we redesign an existing feature. Developers let Experts know beforehand when we’re going to launch features so they can field support issues with current information.</p> <p>There isn’t any central authority that dictates these processes. Each team puts together their process and if things go poorly, they talk about it and improve it. Sometimes that requires talking and changing a process between teams. We do have a few people who float between teams that can view issues in the aggregate and suggest improvements.</p> <h2 id="empowerment">Empowerment</h2> <p>A legacy codebase would never improve if the team working on it didn’t have the authority to make big changes. The freedom to improve areas is a wonderful advantage. But with that advantage comes responsibility: we still have to prioritize features and get a general consensus among the immediate team.</p> <p>I’ve worked in some places where the unspoken rule was “It ain’t broken enough, so don’t fix it” and that is not the case at Harvest. Most of us have our own personal short list of areas we want to make better and have the flexibility to squeeze wins here and there within a normal work week.</p> <p>The team submits and vets bigger feature changes and rewrites, not a Product Manager or a Project Manager, and those projects are prioritized alongside everything else.</p> <h2 id="a-direct-influence">A Direct Influence</h2> <p>The direct influence of everyone on our team on our customers means a lot to me. I like that my work positively affects contractors, designers, business owners, and many others. Our customer’s needs are very real and have serious impacts on their livelihood.</p> <p>Some of our customers are independent with various distractions that pull at them. It’s very satisfying to know that my work can improve their day-to-day life. Our team has a responsibility to keep a stable infrastructure with as few bugs as possible because our screw-ups can cause a big disruption for our customers.</p> <h2 id="a-glimpse">A Glimpse</h2> <p>A recent meeting inspired me to write this post. My team is working with another team on an integration that have some particularly interesting (and fun!) performance implications.</p> <p>We met a few times to try and hammer out how one piece of code should talk to another. These meetings have included Design and Ops to help weigh in with different perspectives. Our last meeting between the teams had five developers focusing on a specific technical issue.</p> <ul> <li>We talked about a few straightforward solutions and we agreed on one along with some metrics.</li> <li>As we learn more, we agreed we might need to bring design back into the discussion in order to figure out some user interface compromises because of performance.</li> <li>We agreed we need to take a serious look at the root cause of the performance issue and prioritize that when we can.</li> <li>We ended the meeting with “Let’s all expect to rewrite parts of this we roll it out 100%. We can consider ourselves lucky if it makes it to production as-is.” That got a few chuckles but everyone nodded in agreement.</li> </ul> <p>This is a great glimpse of what it is like to work here. We know we may have to change as we go. The team is not afraid of being wrong and has the courage to move forward with the information we have. Multiple areas can weigh in with input. The teams are not shutting down the conversation by pointing fingers or huffing “that’s not my problem.”</p> <h2 id="a-coworkers-comment">A Coworker’s Comment</h2> <p>I asked my coworkers to review a draft before I published this. <a href="https://www.getharvest.com/about/meet-the-team#andrew-dunkman">Andrew</a> said parts of this post resonated with him. I liked his comment so much that I asked his permission to end with it.</p> <blockquote> <p>I love watching <a href="http://www.thisoldhouse.com/toh/">This Old House</a> — it’s one of my weekend guilty pleasures. I keep coming back to the show because of the amount of work they put into a project house — it’s incredible. The current project has a closed-in back yard, so when the project called for replacing three stone walls, they had to disassemble the wall and carry it out one wheelbarrow at a time, through the house, to a dumpster on the street.</p> <p>They care for the house. But it’s not just care — it’s a kind of love. My mother used to tell me, “no matter what you do, you’ll be our son, and we love you for that.” I like to think of the craftspeople on This Old House that way — “No matter what kind of mold and crumbling masonry we find, you’ll always be our project house, and we’ll make it right.”</p> </blockquote> Code Reviews at Harvest 2014-10-02T11:29:02-04:00 /blog/2014/10/code-reviews-at-harvest Zach Moazeni zach.moazeni@gmail.com <p><em>Originally posted to <a href="http://techtime.getharvest.com/blog/code-reviews-at-harvest">http://techtime.getharvest.com/blog/code-reviews-at-harvest</a>.</em></p> <p>Let’s face it — code reviews can be tough. Even if your team fully adheres to a certain <a href="https://github.com/bbatsov/ruby-style-guide">style guide</a>, programming is so subjective that smart people can argue great points on conflicting approaches.</p> <p>We use <a href="https://help.github.com/articles/using-pull-requests">GitHub Pull Requests</a> heavily at Harvest and we require code reviews for everything meaningful that goes into production. Here’re a few guidelines that we’ve developed for reviewing each other’s changes so we can stay productive and focused on what’s important — launching code.</p> <h2 id="our-current-process">Our current process</h2> <p>Every company has a different deployment process, but here at Harvest it looks a little something like this:</p> <ol> <li>We discuss what needs to be built, changed, or fixed.</li> <li>A developer/designer creates a branch off of master.</li> <li>They work on their branch. When it’s complete they push it up to GitHub and create a Pull Request tagging an appropriate person or team to review it.</li> <li>One or more tagged people from that team will review the code and give a +1 when they feel it’s ready to be deployed.</li> <li>The original submitter will then merge it into master and deploy it.</li> </ol> <p>Multiple posts could be written about each of these steps. However I’m only going to talk about #3 and #4.</p> <p>We use code reviews at Harvest to help communicate with the team what’s going into production, to help each other learn new tricks and techniques as things evolve, and to point out specific areas to investigate that we may unintentionally break.</p> <h2 id="if-it-fixes-a-serious-bug-just-let-it-go">If it fixes a serious bug, just let it go</h2> <p>We might disagree on approach, but if there’s a serious issue in Harvest that’s affecting a customer, and someone on the team has a <a href="/blog/find-bugs-once-and-the-joy-of-bug-fixing">fix for it</a>, we will always let it ship. We can always circle back and fix it better or more thoroughly later.</p> <h2 id="know-whats-blocking-your-code">Know what’s blocking your code</h2> <p>Code reviews can feel unproductive because they don’t have a clear goal. We’ve put together a survey we call <a href="http://harvesthq.github.io/fias/?v1=1&amp;v2=1&amp;v3=1">FIAS (the Filler Impact Assessment Survey)</a>, a tongue-in-cheek acronym named after <a href="https://www.getharvest.com/about/meet-the-team#patrick-filler">Patrick Filler</a>, the Harvester who proposed the idea.</p> <p>The idea is simple: take an educated guess at ranking three questions on a scale of one to ten:</p> <ul> <li>How much of the app is affected?</li> <li>How much of this change is mysterious to you?</li> <li>How easy is it for you to imagine this performs in an unexpected way after deployment?</li> </ul> <p>Then add up the scores for each question.</p> <ul> <li>If the score is less than fifteen, you only need one person to give you a +1.</li> <li>If the score is fifteen to twenty, you need two people to give you a +1.</li> <li>If the score is over twenty, you need two people and full QA.</li> </ul> <p><em>QA is different for each team. Harvest has staging environments that mirror production and we have scripts for each section that we manually test.</em></p> <p>This isn’t perfect and the results may not make sense for other teams. For example, another team may want three people to +1 or always QA above ten points. However, the point is that, when a Pull Request begins review, we have a clear idea what it will take to launch it to production.</p> <p>We also don’t always follow the FIAS. For example, even if we only need one +1, the submitter may still override the FIAS and ask for two to three +1s because they know their changes involve a particularly tough part of the codebase.</p> <p>Now that we have a set goal, we can work backwards. If some of the changes makes a reviewer uncomfortable, can that part be segmented out and the rest launched instead? Can this branch be merged even if there’s a heated discussion over using the <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">Single Responsibility Principle</a> vs a single clear object?</p> <p>With a simple process, we’ve removed the ambiguity that most code reviews start with.</p> <h2 id="communicate-clearly-to-reviewers-whats-going-on">Communicate clearly to reviewers what’s going on</h2> <p>FIAS is a great tool to get a general sense of how large (and risky) a change is to you — but it isn’t always the best tool for communicating to your reviewers what the change actually entails. Sometimes, <a href="http://www.schneems.com/post/41104255619/use-gifs-in-your-pull-request-for-good-not-evil/">GIFs</a> or annotated before and after images (made with tools like <a href="http://monosnap.com/welcome">Monosnap</a>) can be more effective. Pre-commenting certain areas in your own Pull Request is also helpful: “I know this line seems unrelated, but it is because…”</p> <p>Clearly communicating the change starts the code review on the right foot. The submitter may have invested days working on a change, but the people reviewing it have not.</p> <h2 id="clarify-blocking-comments">Clarify blocking comments</h2> <p>Not every comment or question has to be resolved. Text doesn’t always convey emotion, and it’s easy to misread someone’s intent. It’s perfectly fine to ask a reviewer, “Is this a blocking issue?” Often it isn’t, or it can be handled separately.</p> <p>Pull Requests can also end up being a lightning rod for debate. Discussions among the team can continue on a Pull Request, but they can also be moved elsewhere — to a separate issue, internal tool, or even a meeting.</p> <p>Our reviewers will normally prefix comments with “[NB]” for non-blocking comments: “[NB] This works, but here’s a quick snippet that’s a little more clear”. A simple prefix like that can help speed along code reviews.</p> <h2 id="face-to-face-meetings">Face-to-face meetings</h2> <p>We often raise a white flag and ask for an impromptu face-to-face meeting or a quick chat, usually by spinning up a <a href="https://www.google.com/+/learnmore/hangouts/">Google Hangout</a> or conversation in <a href="https://www.hipchat.com/">HipChat</a>. This seems like an obvious tip. However recognizing the need for these meetings can be tricky. If two people have posted back and forth at least once on the same topic, it will likely be easier to just hash it out face-to-face. If you find yourself writing two paragraphs, some of your concerns will likely get lost. You may be able to convey your thoughts better over audio.</p> <p>It’s nice to see a comment history of how decisions were made. However, you can still accomplish this by posting a quick recap of what was decided in the meeting.</p> <h2 id="dont-limit-the-number-of-reviewers">Don’t limit the number of reviewers</h2> <p>Everyone on the team pitches in with code reviews and we don’t have official reviewers here at Harvest. We may purposefully ping someone who we know has had a lot of experience in a particular area, but we never wait on one person to go through all the reviews.</p> <p>This becomes a bigger deal as the team grows. With an application as large as Harvest, it’s extremely difficult to keep everything in your head. And even if you do feel good about a certain area, it will likely change over time as other people help out with maintenance.</p> <p>We also notify people to give them a heads-up without expecting them to review the pull request. We do this by prefixing “/cc” before their name “/cc @zmoazeni this might affect your work on reports”.</p> <h2 id="not-a-perfect-process">Not a perfect process</h2> <p>We don’t have any delusions that our process is perfect. However all of these points help speed along our code reviews. Taking some of these tips and morphing it to fit your organization may help out your team too — or, if you think your process could help out our team, write up your process and <a href="mailto:zach@getharvest.com">send us a link</a>! We’re always looking to improve.</p> Simple Memoization in Scala 2014-01-02T00:00:00-05:00 /blog/2014/01/simple-memoization-in-scala Zach Moazeni zach.moazeni@gmail.com <p>In Ruby, it’s common to use instance variables and conditional assignment to to concisely <a href="http://en.wikipedia.org/wiki/Memoization">memoize</a> expensive operations.</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">MemoizeSomething</span> <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="vi">@x</span> <span class="o">=</span> <span class="n">x</span> <span class="k">end</span> <span class="k">def</span> <span class="nf">somethingExpensive</span> <span class="vi">@somethingExpensive</span> <span class="o">||=</span> <span class="k">begin</span> <span class="nb">puts</span><span class="p">(</span><span class="s2">"in here, going to sleep to calculate an 'expensive' operation"</span><span class="p">)</span> <span class="nb">sleep</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="s2">"the result: </span><span class="si">#{</span><span class="vi">@x</span><span class="si">}</span><span class="s2">"</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> <span class="n">o</span> <span class="o">=</span> <span class="no">MemoizeSomething</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="s2">"foo"</span><span class="p">)</span> <span class="n">o</span><span class="p">.</span><span class="nf">somethingExpensive</span> <span class="c1"># =&gt; "the result: foo"</span></code></pre></figure> <p>You could reuse the same of strategy of detecting <code class="highlighter-rouge">nil</code>/<code class="highlighter-rouge">null</code> and conditionally evaluating the result. However Scala has lazy evaluation built in. So you could write the following:</p> <figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">class</span> <span class="nc">MemoizeSomething</span><span class="o">(</span><span class="n">x</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="o">{</span> <span class="k">lazy</span> <span class="k">val</span> <span class="n">somethingExpensive</span> <span class="k">=</span> <span class="o">{</span> <span class="n">println</span><span class="o">(</span><span class="s">"in here, going to sleep to calculate an 'expensive' operation"</span><span class="o">)</span> <span class="nc">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="o">(</span><span class="mi">4000</span><span class="o">)</span> <span class="n">s</span><span class="s">"the result: $x"</span> <span class="o">}</span> <span class="o">}</span> <span class="k">val</span> <span class="n">o</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">MemoizeSomething</span><span class="o">(</span><span class="s">"foo"</span><span class="o">)</span> <span class="n">o</span><span class="o">.</span><span class="n">somethingExpensive</span> <span class="o">//</span> <span class="n">res0</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">the</span> <span class="n">result</span><span class="k">:</span> <span class="kt">foo</span></code></pre></figure> <p>When <code class="highlighter-rouge">MemoizeSomething</code> is initialized, it won’t evaluate <code class="highlighter-rouge">somethingExpensive</code> right away, but the first time that variable is requested it will fully evaluate it.</p> <p>If you’re not a fan of the closure above, you could also use a separate method:</p> <figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">class</span> <span class="nc">MemoizeSomething</span><span class="o">(</span><span class="n">x</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="o">{</span> <span class="k">lazy</span> <span class="k">val</span> <span class="n">somethingExpensive</span> <span class="k">=</span> <span class="n">calcSomethingExpensive</span> <span class="k">def</span> <span class="n">calcSomethingExpensive</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="o">{</span> <span class="n">println</span><span class="o">(</span><span class="s">"in here, going to sleep to calculate an 'expensive' operation"</span><span class="o">)</span> <span class="nc">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="o">(</span><span class="mi">4000</span><span class="o">)</span> <span class="n">s</span><span class="s">"the result: $x"</span> <span class="o">}</span> <span class="o">}</span> <span class="k">val</span> <span class="n">o</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">MemoizeSomething</span><span class="o">(</span><span class="s">"foo"</span><span class="o">)</span> <span class="n">o</span><span class="o">.</span><span class="n">somethingExpensive</span> <span class="o">//</span> <span class="n">res0</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">the</span> <span class="n">result</span><span class="k">:</span> <span class="kt">foo</span></code></pre></figure> Detecting Scala Extension Classes at Runtime 2013-12-29T00:00:00-05:00 /blog/2013/12/detecting-scala-extension-classes-at-runtime Zach Moazeni zach.moazeni@gmail.com <p>Years ago, when I left Java for Ruby and Rails, I found Rails’s opinions on <a href="http://en.wikipedia.org/wiki/Convention_over_configuration">convention over configuration</a> very refreshing. I was tired of all the XML files needed for even the simplest Java architectures.</p> <p>If you ignore the overengineered uses of XML, there’s still something lacking with Java compared to Ruby when writing extendable software. Technically both languages are interpretted, however Java’s only entry point is the initial <code class="highlighter-rouge">main()</code> method that runs at the begnning. Constrast that with Ruby, where every file that is loaded has the potential for changing the context of the runtime.</p> <p>Some developers are revolted at Ruby’s flexible nature, and while poorly written code can get squirrely and difficult to understand, I find that open execution context very useful in practice.</p> <p>After coming back to the JVM and working with Scala, I was beginning to miss this feature from Ruby. Scala has an advantage over Java with its support for interpretting uncompiled files, but loading other uncompiled Scala files outside the REPL is difficult.</p> <p>Java has some powerful reflection libraries though, both built into the standard library and without. One of these libraries I came across is <a href="https://github.com/ronmamo/reflections">reflections</a> which provides enough tools to make some magic happen. You can check out some <a href="https://github.com/zmoazeni/reflections-example">example reflection code I put together</a>.</p> <p>The technique is simple.</p> <ol> <li>First we define an <a href="https://github.com/zmoazeni/reflections-example/blob/master/reflections-base/src/main/scala/org/reflections_example/extension/BaseExtension.scala">base interface</a> that becomes an anchor so we can identify extension classes. This example uses a Scala trait, but you could use a Java interface. <em>In my example I also scope all extensions to a particular package for better performance.</em></li> <li>We can then separately compile <a href="https://github.com/zmoazeni/reflections-example/blob/master/reflections-extension/src/main/scala/org/reflections_example/extension/MyFirstExtension.scala">a couple</a> <a href="https://github.com/zmoazeni/reflections-example/blob/master/reflections-extension/src/main/scala/org/reflections_example/extension/MySecondExtension.scala">extension classes</a> against our base code that implement that interface.</li> <li>Now back in our <code class="highlighter-rouge">main()</code> method, we can use the reflections library to <a href="https://github.com/zmoazeni/reflections-example/blob/master/reflections-base/src/main/scala/org/reflections_example/Main.scala#L9-L13">find any extensions at runtime</a>, instantiate them, and run a common method that we define. This allows our extensions to bootstrap themselves however they need. For example, we could set up <a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">pubsub</a> in our original code to allow the extensions to modify the program behavior.</li> </ol> <p>That’s it. With this pattern you can dynamically extend a program at runtime just by dropping a jar in the classpath.</p> <p>I suspect the same developers that abhor Ruby’s per-required-file entry points that I described above will also dislike this strategy. But this just opens up the entry points for other code at runtime. You still have to build a design that lets extensions do anything meaningful.</p> <p>As a concrete application, I’m considering using this for a rewrite of <a href="/blog/2013/04/csscss/">csscss</a>. With this design, I can write a base progam that only deals with CSS, but open up filetype recognition for extensions. Then other developers could write extensions for precompiling <a href="http://sass-lang.com/">SASS</a>, <a href="http://lesscss.org/">LESS</a>, <a href="http://learnboost.github.io/stylus/">Stylus</a> or any other meta CSS language into CSS. And to enable one of those precompilers, a user would only have to download a jar for that extension and put it into a known directory.</p> <p>No XML files. No configuration. It just works.</p> Scala parser performance with v2.10.x 2013-12-28T00:00:00-05:00 /blog/2013/12/scala-v2-10-parser-performance Zach Moazeni zach.moazeni@gmail.com <p>I have been tinkering with rewriting <a href="/blog/2013/04/csscss/">csscss</a> in scala and have really enjoyed the language. Especially after working in Haskell. I noticed however that after upgrading my local java version to the latest v1.7 <em>(currently 1.7 update 45)</em> my parsing performance tanked.</p> <p>My toy parser is regex based, and I asked on the mailing list if there have been any recent regex changes. I was pointed to the following links:</p> <ul> <li><a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6924259">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6924259</a></li> <li><a href="http://java-performance.info/changes-to-string-java-1-7-0_06/">http://java-performance.info/changes-to-string-java-1-7-0_06/</a></li> <li><a href="https://issues.scala-lang.org/browse/SI-7710">https://issues.scala-lang.org/browse/SI-7710</a></li> </ul> <p>Turns out in 1.7 update 6, the semantics of <code class="highlighter-rouge">String</code> changed:</p> <blockquote> <p>String.substring created a String, which shared an internal char[] value with an original String, which allowed you:</p> <ol> <li>To save some memory by sharing character data</li> <li>To run String.substring in a constant time ( O(1) )</li> </ol> <p>At the same time such feature was a source of a possible memory leak…</p> <p><a href="http://java-performance.info/changes-to-string-java-1-7-0_06/">http://java-performance.info/changes-to-string-java-1-7-0_06/</a></p> </blockquote> <p>The problem comes up when parsing in scala because <code class="highlighter-rouge">subSequence</code> (which depends on <code class="highlighter-rouge">substring</code>) is used heavily. So every parsing step would require at least O(n) of copying an array to a new array when creating a String.</p> <p>In the <a href="https://issues.scala-lang.org/browse/SI-7710">scala bug</a>, the reporter mentioned writing a custom <code class="highlighter-rouge">CharSequence</code> that behaved the way same <code class="highlighter-rouge">String</code> used to to reclaim the performance.</p> <p>Here is a snippet that did the trick for me:</p> <figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">import</span> <span class="nn">java.lang.CharSequence</span> <span class="k">class</span> <span class="nc">FastCharSequence</span><span class="o">(</span><span class="n">chars</span><span class="k">:</span> <span class="kt">Array</span><span class="o">[</span><span class="kt">Char</span><span class="o">],</span> <span class="k">val</span> <span class="n">startBounds</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="k">val</span> <span class="n">endBounds</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span> <span class="k">extends</span> <span class="nc">CharSequence</span> <span class="o">{</span> <span class="k">def</span> <span class="k">this</span><span class="o">(</span><span class="n">chars</span><span class="k">:</span> <span class="kt">Array</span><span class="o">[</span><span class="kt">Char</span><span class="o">])</span> <span class="k">=</span> <span class="k">this</span><span class="o">(</span><span class="n">chars</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">chars</span><span class="o">.</span><span class="n">length</span><span class="o">)</span> <span class="k">def</span> <span class="k">this</span><span class="o">(</span><span class="n">input</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span> <span class="k">=</span> <span class="k">this</span><span class="o">(</span><span class="n">input</span><span class="o">.</span><span class="n">toCharArray</span><span class="o">)</span> <span class="k">def</span> <span class="n">length</span><span class="o">()</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">endBounds</span> <span class="o">-</span> <span class="n">startBounds</span> <span class="k">def</span> <span class="n">charAt</span><span class="o">(</span><span class="n">index</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Char</span> <span class="o">=</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="n">length</span><span class="o">)</span> <span class="o">{</span> <span class="n">chars</span><span class="o">(</span><span class="n">index</span> <span class="o">+</span> <span class="n">startBounds</span><span class="o">)</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">s</span><span class="s">"$boundsInfo index: $index"</span><span class="o">)</span> <span class="o">}</span> <span class="o">}</span> <span class="k">def</span> <span class="n">subSequence</span><span class="o">(</span><span class="n">start</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">end</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">CharSequence</span> <span class="o">=</span> <span class="o">{</span> <span class="k">if</span> <span class="o">(</span><span class="n">start</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">start</span> <span class="o">&lt;=</span> <span class="n">length</span> <span class="o">&amp;&amp;</span> <span class="n">end</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">end</span> <span class="o">&lt;=</span> <span class="n">length</span><span class="o">)</span> <span class="o">{</span> <span class="k">new</span> <span class="nc">FastCharSequence</span><span class="o">(</span><span class="n">chars</span><span class="o">,</span> <span class="n">startBounds</span> <span class="o">+</span> <span class="n">start</span><span class="o">,</span> <span class="n">startBounds</span> <span class="o">+</span> <span class="n">end</span><span class="o">)</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">IndexOutOfBoundsException</span><span class="o">(</span><span class="n">s</span><span class="s">"$boundsInfo start: $start, end $end"</span><span class="o">)</span> <span class="o">}</span> <span class="o">}</span> <span class="k">override</span> <span class="k">def</span> <span class="n">toString</span><span class="o">()</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">chars</span><span class="o">,</span> <span class="n">startBounds</span><span class="o">,</span> <span class="n">length</span><span class="o">)</span> <span class="k">private</span> <span class="k">def</span> <span class="n">boundsInfo</span> <span class="k">=</span> <span class="n">s</span><span class="s">"current: (startBounds: $startBounds, endBounds: $endBounds, length: $length, chars length: ${chars.length})"</span> <span class="o">}</span></code></pre></figure> <p>Instead of parsing a String, you can wrap this around the String and parse that to reclaim performance.</p> <p>This strategy may be <a href="https://issues.scala-lang.org/browse/SI-7710">brought into scala v2.11</a>, which should be released soon. But until then, you can use something similar.</p> Why csscss doesn't remove duplication for you 2013-04-27T00:00:00-04:00 /blog/2013/04/why-csscss-doesnt-remove-duplication-for-you Zach Moazeni zach.moazeni@gmail.com <p>After I released <a href="http://zmoazeni.github.io/csscss/">csscss</a>, I’ve been asked numerous times:</p> <blockquote> <p>Why doesn’t csscss remove duplication for me?</p> </blockquote> <p>That is a fair question. It is easy to look at csscss and ask why it isn’t more like a <a href="http://en.wikipedia.org/wiki/Minification_(programming\)">minifier</a> that a developer can just run before deploying updates. In fact <a href="http://css.github.io/csso/">CSSO</a> and <a href="https://github.com/begriffs/css-ratiocinator">css-ractionator</a> are projects that do this exact thing.</p> <p>So why didn’t I build it that way?</p> <h2 id="csscss-forces-me-consider-my-code-design">csscss forces me consider my (code) design</h2> <p>I built csscss as a tool to help a developer write smarter CSS. Duplication isn’t all bad, but we often write duplicated CSS unknowingly. When I see duplicated CSS, it forces me to think about my rules and structure. It forces me to ask myself several questions about my code.</p> <p>“Is this necessary?”</p> <p>“Have I already defined a structure for this? Do I need to extend existing structures?”</p> <p>“Is there a better way to define the same rules?”</p> <p>“Can I leverage other CSS tools to better structure these styles? (<a href="http://sass-lang.com/">Sass</a>, <a href="http://lesscss.org/">LESS</a>, etc)”</p> <p>All of these answers reduce duplication, but more importantly, they force me to consider the design of my code. And I don’t think there is a single algorithm that satisfies all cases.</p> <p>Now you can obviously do this without an automated tool. And it is easy for a small number of rules. However as your site/application grows it is extremely difficult to continue this mental juggling.</p> <p>I like to think of csscss as a tool that helps <strong>extend my intuition</strong>, not replace it.</p> <h2 id="generated-code-is-harder-to-debug">Generated code is harder to debug</h2> <p>If csscss generated new duplication-free code, all I am doing is punting work for later. Which usually comes up when I need to debug my styles from the browser. If the generated output is substantially different than the original source, it makes my job harder to fix my problems.</p> <p>I have a similar problem with <a href="http://coffeescript.org/">coffeescript</a>, though I still enjoy using it. The coffeescript I write and the javascript I debug are different. Luckily, in that case I can follow the flow enough to jump back to coffeescript to fix my bugs.</p> <p>However the point of csscss is not to optimize code, it is to help inform you about areas improvement. If csscss did this for you, I expect the gulf between original source and browser CSS to be rather large.</p> <h2 id="improving-the-css-landscape">Improving the CSS landscape</h2> <p>None of this is a knock against other developers working on the same problem. In fact, I think different perspectives and trial/error in this field can only be a good thing. CSS has been around for over a decade and it is surprising to me that we don’t have more tools. So I commend anyone spending time to contribute. csscss is my contribution based on my opinions.</p> <p>Here are some others tools that people have mentioned since I released csscss.</p> <ul> <li><a href="http://css.github.io/csso/">CSSO</a></li> <li><a href="https://github.com/begriffs/css-ratiocinator">css-ractionator</a></li> <li><a href="https://github.com/visionmedia/rework">rework</a></li> <li><a href="https://github.com/geuis/helium-css">helium</a></li> </ul> csscss 2013-04-25T00:00:00-04:00 /blog/2013/04/csscss Zach Moazeni zach.moazeni@gmail.com <p>About eight months ago, I had an idea for a potential tool that could help refactor <a href="https://en.wikipedia.org/wiki/Cascading_Style_Sheets">CSS</a>. I wondered, “Why aren’t there many (any?) CSS static code analyzers out there?”</p> <p>I mean when you think about it, it is a little crazy. CSS is a pretty simple language and it has been around for over 15 years. The <a href="https://developer.mozilla.org/en-US/docs/CSS/Getting_Started/Selectors">selectors</a> can get a little intense and there is a laundry list of properties. But the core syntax of CSS is pretty straightforward.</p> <p>The premise was simple. In all other languages, I work at writing <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY</a> code. So why can’t something inform me when I’m repeating the same declarations over and over? Better yet, I wouldn’t need a browser to figure this out. I should have all information immediately available in the source code.</p> <p>I had this idea eight months ago, and I am proud that I had the fortitude to stick with the project and launch it. So here it is.</p> <p>World, please meet <a href="http://zmoazeni.github.io/csscss/">csscss</a>.</p> Your Identity ≠ Your Code 2013-04-24T00:00:00-04:00 /blog/2013/04/your-identity-your-code Zach Moazeni zach.moazeni@gmail.com <p><em>This was originally posted on <a href="http://collectiveidea.com/blog/archives/2012/03/16/your-identity-your-code/">March 16, 2012</a> when I worked with CollectiveIdea</em></p> <p>This morning I started my day seeing this in my twitter feed:</p> <blockquote class="twitter-tweet"><p>Your identity ≠ Your job</p>&mdash; Kyle Steed (@kylesteed) <a href="https://twitter.com/kylesteed/status/180651842919858176">March 16, 2012</a></blockquote> <script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script> <p>I’ve been thinking about this idea a lot lately. And I think it’s a trap that developers (myself included) easily fall into. To make this idea a little more specific to developers: Your identity ≠ Your code.</p> <p>As developers, we like to solve problems and get a kick out of that feeling that seemingly comes out of nowhere: “Yes! I solved it. I am all that is awesome!” (maybe I’m the only one who says that out loud). But at the same time we attach ourselves tightly to our code and will equate criticisms of our code as criticisms of us as a person. And lets be honest, while we all want to improve, it is difficult not to equate “this could be better” with “you should have been smarter”.</p> <p>I don’t think it is entirely due to criticism either. There are developers that I look up to, and I still get geeked out when I’m lucky enough to talk with them. For most of my geek-heroes, I also see prominent projects that they are part of, and it is difficult to separate “they are awesome because they wrote x”. I know when I first began, I cared a lot more about how people would perceive the code I wrote rather than how well it solved the problem at hand. It is more important to view your web heros as people who ship working software. If you think they are the authority on how software should be written, you’re in for an awakening.</p> <p>This idea also affects open source. It is rare to see a young developer submit bug fixes to existing projects. Most developers have the talent to fix outstanding issues, but don’t due to the fear of rejection and criticism. And again it isn’t the specific criticisms of the code they write, which in all likelihood would only accelerate their learning, it’s hearing those criticisms and taking it personal. It is hard not to take these criticisms personal, especially when you read someone else’s code and say “What the hell was this fella thinking? That moron.” Believe it or not, I’ve caught myself saying that only to run a <code class="highlighter-rouge">git blame</code> and discover I was the “moron” who wrote the code. Early in my career, after uttering something similar, I was told by another developer “Criticize the code, not the developer.” And that has stuck, though at times I have to remind myself.</p> <p>We will look at a piece of code and think “This is God-awful. This dev is terrible”. And it’s important to remember there is a lot more context around why code is written a certain way. The original developer’s experience does have an effect, but think back to the hacks you have written and why they were necessary. Even if it can be written better, you’re not doing anyone favors by slamming the dev.</p> <p>But then we wonder why we get defensive when we hear people sharing ideas about the code we write. “Yeah, they’re saying it politely, but I know what they’re really thinking.”</p> <p>Cut the ego. Stop equating bad code with bad developers. Stop equating code criticisms as a knock against you as a person. We could use more people submitting ideas and less animosity around existing code.</p> ActiveResource XML bug fix for Rails 3.0.19 2013-01-10T10:24:49-05:00 /blog/2013/01/activeresource-xml-bug-fix-for-rails-3-dot-0-19 Zach Moazeni zach.moazeni@gmail.com <p><em>Originally posted to <a href="http://techtime.getharvest.com/blog/activeresource-xml-bug-fix-for-rails-3-dot-0-19">http://techtime.getharvest.com/blog/activeresource-xml-bug-fix-for-rails-3-dot-0-19</a>.</em></p> <p>The past two weeks have been abuzz with security patches for Rails. <a href="http://weblog.rubyonrails.org/2013/1/8/Rails-3-2-11-3-1-10-3-0-19-and-2-3-15-have-been-released/">Yesterday’s in particular</a> is quite serious, and if you haven’t upgraded yet, you really should.</p> <p>This morning, we noticed an issue with a few of our applications that are still running Rails v3.0.x. There is currently a bug for security-patched Rails v3.0 applications serving XML data to ActiveResource consumers (think of a typical Rails XML API).</p> <p>This bug was noticed <a href="https://github.com/rails/rails/pull/492">2 years ago</a> by <a href="https://github.com/jaw6">jaw6</a> and his fix was committed to Rails v3.2 and v3.1, but not v3.0. This wasn’t an issue until yesterday’s security upgrade.</p> <p>Now if the latest version of ActiveResource requests XML data from a Rails v3.0 server, they may raise an odd error <code class="highlighter-rouge">Hash::DisallowedType: Disallowed type attribute: "yaml"</code></p> <p>We have just had a <a href="https://github.com/rails/rails/pull/8853">pull request</a> merged into Rails that will fix this issue in v3.0.20 and should be released soon. Until then, if you need to apply this immediately you can have Bundler use this code directly from GitHub:</p> <pre><code> # In your Gemfile gem "rails", :git =&gt; 'https://github.com/rails/rails.git', :branch =&gt; '3-0-stable' </code></pre> My Thoughts on Refinements 2012-11-19T23:37:26-05:00 /blog/2012/11/my-thoughts-on-refinements Zach Moazeni zach.moazeni@gmail.com <p>I have followed the various Ruby 2.0 refinements discussion for a little while now. There is certainly a lot of talk in the air for and against the inclusion of this feature. For those tuning in, here are some notable links to get you up to speed:</p> <ul> <li><a href="https://speakerdeck.com/a_matsuda/ruby-2-dot-0-on-rails">A presentation by Akira Matsuda at RubyConf 2012</a> introducing refinements along with other interesting new Ruby 2.0 (potential) features.</li> <li><a href="http://bugs.ruby-lang.org/issues/4085">A thorough discussion</a> by Ruby runtime developers</li> <li><a href="http://www.confreaks.com/videos/1278-rubyconf2012-toward-a-design-for-ruby">Another RubyConf 2012 talk</a> by Brian Ford a prominent <a href="http://rubini.us/">Rubinius</a> developer.</li> <li><a href="http://blog.headius.com/2012/11/refining-ruby.html">A blog post</a> by Charles Oliver Nutter a prominent <a href="http://jruby.org/">JRuby</a> developer.</li> </ul> <p>I want to start off by saying, I have a ton of respect for the incredible work all developers have put into making my job a reality. The community wouldn’t be where it is without all your hard work and sweat. And my career certainly depends on the fruits of your labor. Thank you.</p> <p>There is an interesting mix of pros and cons for the inclusion of refinements in Ruby 2.0. I’m going to distil what I’ve read and my comments on the matter.</p> <h2 id="current-discussions">Current discussions</h2> <h3 id="implementation-difficulties-within-mrijrubyrubiniusother">Implementation difficulties within MRI/JRuby/Rubinius/other</h3> <p>I’m just not qualified to weigh in on this area. I understand that there are some (many?) challenges in implementing this fully without breaking the <a href="http://en.wikipedia.org/wiki/Principle_of_least_astonishment">principal of least surprise</a> for developers. That may or may not sway everything else. It should certainly not be overlooked, but this isn’t a scenario where you have people proposing code without writing it. Runtime developers are currently debating the feasibility implementation, and it does look split.</p> <h3 id="crazy-use-cases">Crazy use cases</h3> <p>Many are cited on <a href="http://blog.headius.com/2012/11/refining-ruby.html">Charles’ post</a> such as:</p> <p>What does the following do?</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Baz</span> <span class="o">&lt;</span> <span class="no">Quux</span> <span class="k">def</span> <span class="nf">up_and_add</span><span class="p">(</span><span class="n">str1</span><span class="p">,</span> <span class="n">str2</span><span class="p">)</span> <span class="n">str1</span><span class="p">.</span><span class="nf">upcase</span> <span class="o">+</span> <span class="n">str2</span><span class="p">.</span><span class="nf">upcase</span> <span class="k">end</span> <span class="k">end</span></code></pre></figure> <p>Josh Ballanco’s example, also from Charles’s post where</p> <blockquote> <p>“the following code only refines the “bar” method, not the “foo” method.”</p> </blockquote> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Yummy</span> <span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="n">str</span><span class="p">.</span><span class="nf">camelize</span> <span class="c1"># will error</span> <span class="k">end</span> <span class="n">using</span> <span class="no">StringCamelize</span> <span class="k">def</span> <span class="nf">bar</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="n">str</span><span class="p">.</span><span class="nf">camelize</span> <span class="k">end</span> <span class="k">end</span></code></pre></figure> <p>I strongly dislike this argument. In complete fairness, you have no idea what <code class="highlighter-rouge">String#upcase</code> is going to do. Someone before the call may or may not have overriden the method. It could be <a href="http://apidock.com/rails/ActiveSupport/CoreExtensions/Module/alias_method_chain"><code class="highlighter-rouge">alias_method_chain'ed</code></a> to email my grandmother stock quotes upon invocation. No one knows. But we still love Ruby, we still get work done, and we still ship code.</p> <p>Thinking of obtuse edgecases on the nastiness or confusion this could cause does not dismiss the potential possibilities. My initial reaction, and I think this is shared with many developers, is “Wow, this could really clean up some global-level monkey patching.” That doesn’t mean it will be used only for the forces of good.</p> <p>Myron <a href="http://blog.headius.com/2012/11/refining-ruby.html?showComment=1353368146551#c3690449280229458677">commented with some guidelines</a> he uses when using monkey patching (which I think are great). But why wouldn’t the same apply to refinements?</p> <p>Saying that refinements could lead to dangerous and unusuable code does not dimiss the potential benefits of an incredibly flexible feature. Describe some of the existing features to a non-Ruby developer and watch their face either light up in possibility or scowl in disbelief. Like re-opening classes</p> <h3 id="whats-wrong-with-monkey-patching">What’s wrong with monkey patching?</h3> <p>Brian Ford has asked this many times, and I don’t think this is a great question to lead to the dismissal of refinements. Monkey patching is global and refinements are local to the call stack. I have not seen many great arguments won based on the fact that we can accomplish the same behavior through global means.</p> <h2 id="things-we-should-be-discussing">Things we should be discussing</h2> <p>Aside from language-level implementation details which some language developers seem to think isn’t a problem and others do, I don’t think the many of the current questions and discussions are productive. These are the topics I would like to hear discussed.</p> <h3 id="is-ruby-too-mature-to-include-something-so-experimental">Is Ruby too mature to include something so experimental?</h3> <p>This might seem like a trollish question, but I think needs serious consideration. Ruby is not a young language with a small userbase. Sure we might be small when compared to the sea of C code written and deployed. But there is much more Ruby code in active use now than 5 years ago.</p> <p>Changes this large has an affect on both the users who choose to use it, as well as those who don’t. Are we reaching the point where this is too extreme? If so, where does an idea/implementation like this belong?</p> <h3 id="ruby-compatibility">Ruby compatibility</h3> <p>The implementation of refinements was first proposed and then implemented by Shugo Maeda in MRI. Both JRuby and Rubinius have been fighting for <a href="http://rubyspec.org/">RubySpec</a> for many years, and there has been no effort to spec this feature.</p> <p>Think about that. A prominent MRI runtime developer thinks about an implements an interesting feature, it is brought into the most popular runtime used, and the developers tasked with keeping up compatible runtimes are now forced to implement the same feature in their respective environments.</p> <p>I get why JRuby and Rubinius developers would not be terribly motivated to do that without an argument.</p> <p>Here is where I think MRI and JRuby/Rubinius have competing goals. MRI is continuing to push the boundaries of how to express the Ruby language in new ways. Whereas JRuby/Rubinius are forced to play catch up with large features. I don’t mean that a knock against JRuby and Rubinius, because they certainly bring their own unique things to the table. JRuby has an amazing capacity for concurrency by leveraging the <a href="http://en.wikipedia.org/wiki/Java_virtual_machine">JVM</a> and Rubinius has rethought even the simplest things such as a <a href="http://rubini.us/doc/en/tools/debugger/">fully supported debugger</a>.</p> <p>On top of playing catch up, JRuby/Rubinius continues to advocate to developers to try their runtime and highlight successful production uses. It’s difficult to convince developers to use your runtime if you have to change a large swath of code in the process.</p> <p>This doesn’t mean JRuby/Rubinius developers are fully biased against new ideas. It just means to me that they are heavily incentivized to fight highly experimental features coming from a different runtime. Like refinements.</p> <h3 id="communication">Communication</h3> <p>The number one thing I think we should be discussing is “How did we get into this situation?” Many developers think refinements can be used in good/productive ways (myself included), but why are many smart, respected developers vehementely against it? Apart from language maturity or incentives for/against, I think this entire argument suffers from what countless arguments in the past suffer from: lack of communication.</p> <p>MRI is largely written by Japanese developers, but has become heavily adopted in the United States. JRuby/Rubinius developers are predominately English speaking. There are many developers who speak multiple languages, but not enough.</p> <p>Brian Ford mentioned this in the <a href="http://www.confreaks.com/videos/1278-rubyconf2012-toward-a-design-for-ruby">talk I listed above</a>, though I think he could have presented that topic in a much more tactful, less rushed way. He proposes that we should all be discussing these features in English.</p> <p>If I was a Japanese MRI developer, I would be offended by the way that was presented. First, there are many Japanese developers who have done a great job discussing very complex topics in English, just browse <a href="http://bugs.ruby-lang.org/issues">ruby-lang</a>. I think they should be highlighted, or at least given some sort of props for their efforts.</p> <p>Second, I can’t help but put myself in their shoes. If I built some awesome wizbat that I continued to care for but over the course of time happened to be largely adopted by Russian developers. I would be a bit upset if they criticized me for not discussing development issues in Russian. Nothing against Russian, I just don’t speak a lick of it.</p> <p>On the other side of the coin, developers of JRuby and Rubinius are being forced to implement some very difficult features in their runtimes. Reading the dialog, it sounds like Charles’ and Brian’s complaints are being heard but perhaps not discussed as thoroughly as they probably should be.</p> <p>I do not have any good suggestions for this problem. But I do think this is the crux of it. In fact, this is most likely a frustration that has been worked through for many years and refinements has now been the place to plant the flag and have it out.</p> <h2 id="should-refinements-be-in-ruby-20">Should refinements be in Ruby 2.0?</h2> <p>I think refinements offer some very interesting possibilities. It might turn out to be a disaster or the to key to triggering some amazing innovation in a caffeine-hyped developer. But instead of dividing the community by campaigning for or against its inclusion. I think we should be discussing matters that led us here, and will inevitably lead us to other disagreements if left unresolved.</p> Big Changes Again. Joining Collective Idea. 2011-02-03T11:08:50-05:00 /blog/2011/02/big-changes-again-joining-collective-idea Zach Moazeni zach.moazeni@gmail.com <p>Last year I felt compelled to start <a href="/blog/2010/05/big-changes-going-independent/">Connection Required</a>. Not because I was forced to, but because I wanted to prove to myself that I could start and sustain a business. There is a sense of pride that comes with helping clients and making money from your efforts. I’ve encountered many surprises over the past year, mostly positive. This month, I’m going to be making another big transition and I’m joining <a href="http://collectiveidea.com/">Collective Idea</a>.</p> <p>Why would I join another company rather than sticking with Connection Required? It isn’t because I am unable make it work. I’ve launched several successful projects, made my clients extremely happy and made money in the process. My family was able to move back to Northern Michigan. I’ve been able to spend more time at home than ever before. And I have had to push away as much work as I’ve accepted. All in all, I consider that successful.</p> <p>However, when I consider what I want Connection Required to become, I pause. I do great work, but that doesn’t mean I want to grow a consultancy and start hiring other developers. Enter <a href="http://collectiveidea.com/">Collective Idea</a>. I’ve known these guys for a while. They’re an extremely sharp group and they build great software. At <a href="http://collectiveidea.com/">Collective Idea</a>, I can continue to do what I do best: write great software and make clients happy. But instead of being solo, I am able to join a team of developers who share that same goal. For those who have been in my shoes, you’ll know, that’s a big deal.</p> <p>For those who haven’t been in this situation, at a certain point you realize how few hours you actually have in a day/month/year. Sure I can work more, but then that takes away from my family. I can start hiring, delegating, and leading others, but that requires growing. And I’m pretty confident I’d make an awful boss :). There is only so much I can do as an individual, but as part of a great team I can be a part of awesome.</p> <p>I’m excited about this transition because it’s a step forward on many fronts. You can also read the <a href="http://collectiveidea.com/blog/archives/2011/02/03/welcome-zach-moazeni/">announcement on Collective Idea’s site</a>.</p> <p>Feel free to contact me at my new email address zach -at- collectiveidea.com. This site will continue to stay up, but will morph back into a blog over time.</p> Sharing a desk and internet 2011-01-19T15:31:05-05:00 /blog/2011/01/sharing-a-desk-and-internet Zach Moazeni zach.moazeni@gmail.com <p>After two months in the new office, I realized we’re going to need to a lot more stuff to fill up the space: couch, big screen, etc. You know, the essentials?</p> <p>Then I realized I could just bring in a new desk and offer up the space/internet for people who are passing through. Especially, since there are no <a href="http://en.wikipedia.org/wiki/Coworking">coworking spaces</a> in Traverse City.</p> <p>So if you’re in or around <a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=Traverse+City,+MI&amp;aq=&amp;sll=37.0625,-95.677068&amp;sspn=47.033113,68.291016&amp;ie=UTF8&amp;hq=&amp;hnear=Traverse+City,+Grand+Traverse,+Michigan&amp;z=13">Traverse City</a> / <a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=Elk+Rapids,+MI&amp;sll=44.763057,-85.620632&amp;sspn=0.082881,0.133381&amp;ie=UTF8&amp;hq=&amp;hnear=Elk+Rapids,+Antrim,+Michigan&amp;z=14">Elk Rapids</a>, feel free to stop by. I do ask that you sign in through <a href="http://desksnear.me/workplaces/497-connection-required">DesksNearMe</a> beforehand so we don’t overcommit the space.</p> New iPad App Deployed: Spatik 2010-12-23T12:40:23-05:00 /blog/2010/12/new-ipad-app-deployed-spatik Zach Moazeni zach.moazeni@gmail.com <p>For the past few months I’ve been helping <a href="http://twitter.com/#!/nicholaspatten">Nicholas Patten</a> take his vision and translate it into an iPad App. I’m proud to announce <a href="http://itunes.apple.com/us/app/spatik/id411006823?mt=8">Spatik</a> was accepted today and is now available in the App Store.</p> <p>It’s purpose is to bring Google Reader and Twitter to a user’s fingertips (literally) and let the users schedule tweets. Spatik was a lot of fun to work on for me for a variety of reasons. While developing Spatik I learned more about:</p> <ul> <li>Google’s OAuth workflow, I had previously worked on Twitter OAuth on other projects.</li> <li>The OAuth workflow within the iOS environment.</li> <li>Having an iOS client interact with a custom backend server.</li> <li>The App Store approval process.</li> <li>The iOS human user interface guidelines and building custom interface components.</li> </ul> <p>I will post more about the development lessons learned in other posts. But right now, I want the spotlight squarely on <a href="http://itunes.apple.com/us/app/spatik/id411006823?mt=8">Spatik</a> and <a href="http://twitter.com/#!/nicholaspatten">Nicholas Patten</a>.</p> <p>Check out the app, give it a spin!</p> Tweetstream is now Web Scale 2010-12-22T14:14:51-05:00 /blog/2010/12/tweetstream-is-now-web-scale Zach Moazeni zach.moazeni@gmail.com <p>Over the past few months, I have been working with <a href="http://downstreamapp.com">Downstream</a> on one of their products <a href="http://tweetstreamapp.com">Tweetstream</a>. In addition to building features, we have experienced a significant issue with the infrastructure.</p> <p>Last night we pushed live a slew of updates and as a result the application is much speedier. The guys were so thrilled with the result that they wanted me to write a geeky technical post about all the changes for their users. You can read the entire article <a href="http://blog.downstreamapp.com/post/2418156381/tweetstream-is-now-web-scale">on their blog</a> and also discuss it on <a href="http://news.ycombinator.com/item?id=2032126">Hacker News</a></p> December Northern Michigan Web and Mobile Meetup 2010-12-16T11:37:53-05:00 /blog/2010/12/december-northern-michigan-web-and-mobile-meetup Zach Moazeni zach.moazeni@gmail.com <p>Last night we had our <a href="http://www.meetup.com/nmich-web-mobile/calendar/15499042/">December Michigan Web and Mobile Meetup</a>. We only had a couple guys due to family commitments, but that’s understandable since we’re only a week away from Christmas.</p> <p><a id="more"></a><a id="more-463"></a></p> <p>In the talk from <a href="/blog/2010/11/november-northern-michigan-web-and-mobile-meetup/">last month</a>, Gary Vaynerchuk mentioned Zappos and hearing about their rich company culture, we watched an interview by the CEO of Zappos, Tony Hsieh.</p> <p>Before we watched the interview, we actually watched Jeff Bezos of Amazon give a short clip when they acquired Zappos:</p> <object width="600" height="385"><param name="movie" value="http://www.youtube.com/v/-hxX_Q5CnaA?fs=1&amp;hl=en_US" />&lt;/param&gt;<param name="allowFullScreen" value="true" />&lt;/param&gt;<param name="allowscriptaccess" value="always" />&lt;/param&gt;<embed src="http://www.youtube.com/v/-hxX_Q5CnaA?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="600" height="385" />&lt;/embed&gt;</object> <p><a href="http://bigthink.com/ideas/24384">Here’s Tony’s interview</a> on <a href="http://bigthink.com/">BigThink.com</a>.</p> <p>Tony speaks a lot differently than Gary, but has a lot of insight on Business and Happiness. He mentioned <a href="http://www.amazon.com/Good-Great-Companies-Leap-Others/dp/0066620996">Good to Great</a> and having read that a few years ago, I heartily recommend it as well. He also mentioned <a href="http://www.amazon.com/Tribal-Leadership-Leveraging-Thriving-Organization/dp/0061251305">Tribal Leadership</a> which I’ll be picking up soon. And of course there’s Tony’s book <a href="http://www.amazon.com/Delivering-Happiness-Profits-Passion-Purpose/dp/0446563048">Delivering Happiness</a> which I still need to check out.</p> <p>I really appreciate Tony’s perspective on culture and happiness. I also connected with his comments about the <a href="http://en.wikipedia.org/wiki/Generation_Y">Millennials</a>.</p> <blockquote> <p>…there are so many people in corporate America where they’re a different person at home on weekends versus on monday when they’re in the office. And they leave a little part of themselves, or a big part of themselves at home … maybe what the millennials are bringing to the forefront [is that] they can be the same person…</p> </blockquote> November Northern Michigan Web and Mobile Meetup 2010-11-19T09:06:46-05:00 /blog/2010/11/november-northern-michigan-web-and-mobile-meetup Zach Moazeni zach.moazeni@gmail.com <p>Last night we had our <a href="http://www.meetup.com/nmich-web-mobile/calendar/15107332/">second Northern Michigan Web and Mobile Meetup</a> and I think this went better than our <a href="/blog/2010/11/first-northern-michigan-web-and-mobile-meetup">first</a>. This week I grabbed a couple pizzas from <a href="http://www.chefcharlesinc.com/">Chef Charles in Elk Rapids</a>, that guy can make a tasty pie. In fact I still have a couple slices leftover for lunch today. This week we watched <a href="http://garyvaynerchuk.com/">Gary Vaynerchuk’s</a> RailsConf 2010 talk.</p> <object width="600" height="385"><param name="movie" value="http://www.youtube.com/v/-QWHkcCP3tA?fs=1&amp;hl=en_US" />&lt;/param&gt;<param name="allowFullScreen" value="true" />&lt;/param&gt;<param name="allowscriptaccess" value="always" />&lt;/param&gt;<embed src="http://www.youtube.com/v/-QWHkcCP3tA?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="600" height="385" />&lt;/embed&gt;</object> <p>Gary has great stage presence, and I really connect with his messages in this talk. It also sounded like the group resonated with the talk and fed off his energy. We discussed items like caring about client relationships, the “Mom and Pop” attitude for tech and business, and hustling products.</p> <p>I’ve set a date for <a href="http://www.meetup.com/nmich-web-mobile/calendar/15499042">the next meetup for December 14th</a>. We may be at <a href="http://www.citymac.net/">CityMac in Traverse City</a> again, but I think it may be fun to mix it up and try out the Library. I’ll firm up the location closer to the 14th.</p> <p>If you’re interested in Web, Mobile, and Business, I encourage you to stop by and hang out!</p> First Northern Michigan Web and Mobile Meetup 2010-11-02T09:31:07-04:00 /blog/2010/11/first-northern-michigan-web-and-mobile-meetup Zach Moazeni zach.moazeni@gmail.com <p>Last Thursday we had our first <a href="http://www.meetup.com/nmich-web-mobile/">Northern Michigan Web and Mobile Meetup</a> meeting. While eating pizza, we watched <a href="http://bigthink.com/ideas/18516">an interview with Jason Fried</a> and discussed it afterwards.</p> <script src="http://video.bigthink.com/player.js?deepLinkEmbedCode=dhNG42MTrKizs8l5v500roLKkUKF-JNU&amp;width=516&amp;autoplay=0&amp;height=290&amp;embedCode=dhNG42MTrKizs8l5v500roLKkUKF-JNU"></script> <p>I had a pretty good time, and received some nice compliments on the format. Talking with the group, we may continue to watch and discuss interviews. I’ve set a date for the <a href="http://www.meetup.com/nmich-web-mobile/calendar/15107332/">next meetup on November 18th</a>. I’m still considering where we should go for the venue. CityMac was accommodating, but a few mentioned the meeting rooms in the Library.</p> <p>If you’re in the Traverse City area on the 18th, I encourage you to drop by!</p> HarvestApp.com Impresses Me Again 2010-10-21T17:12:44-04:00 /blog/2010/10/harvestapp-com-impresses-me-again Zach Moazeni zach.moazeni@gmail.com <p>I’ve been using <a href="http://www.getharvest.com/">Harvest</a> for a few years now, and as a customer have been a big fan of their product. A couple months ago they made a <a href="http://www.getharvest.com/blog/2010/08/secure-connection-for-all-accounts/">change to their plans</a> and impressed me again. This time though, it wasn’t as a customer, it was as a developer and business owner.</p> <p>It wasn’t the details of the change that caught my eye, it was that prior to the announcement, they sent me an email regarding how the change affected <a href="http://github.com/zmoazeni/harvested">an open source library</a> I maintain.</p> <p>Here’s the email, posted with permission from Harvest:</p> <blockquote> <blockquote> <p>Hi Zach</p> <p>I’m reaching out early, before the official notification about this, to ask a question about your Harvest client: http://simplechatter.com/2010/04/harvested-a-new-ruby-api-wrapper/</p> <p>In a couple of weeks Harvest will be implementing SSL across the board for all Harvest accounts. Accounts can opt-out of SSL (through Harvest support) but HTTPS will become the default for all Harvest access.</p> <p>The net effect for accounts currently using non-SSL HTTP will be an HTTP 301 redirect from http://subdomain.harvestapp.com to https://subdomain.harvestapp.com when this change goes into effect for everyone.</p> <p>Does your client support this 301 redirect, and SSL in general? If not, we’d like to work with you to support this, to avoid any issues for your access, or for anyone using your client.</p> <p>Please let me know and feel free to contact me for any details, or if I can help in any way.</p> <p>Thanks!</p> </blockquote> </blockquote> <p>The fact that the company not only keeps tabs on the current software community around their product, and intentionally reaches out to them for potential issues, <strong>and</strong> offers to help out to fix those issues <em>speaks volumes</em>. Fostering a community and enabling evangelists around your product is often overlooked and under-prioritized.</p> <p>Kudos guys, and keep up the good work.</p> <p>–</p> <p>Feel free to discuss this here and on <a href="http://news.ycombinator.com/item?id=1817484">Hacker News</a></p> The Pain of Launching Your Own Projects 2010-10-19T09:35:04-04:00 /blog/2010/10/the-pain-of-launching-your-own-projects Zach Moazeni zach.moazeni@gmail.com <p>Ask anyone who has redesigned their own website or released an open source project, pushing something live can be painful. I think there are two primary reasons for this:</p> <ol> <li>concern about making a great first impressions</li> <li>the lack of constraints that are normally in place when working on client projects.</li> </ol> <p>Launching and redesigning your own website is <em>mentally tough</em>. It’s rarely a technical problem. Since it’s your own site and no one paying you to build it, it can be very easy to keep adding features and polishing. Both of which delay the launch date.</p> <p>Although they are often the source of stress, budgets and deadlines are useful. I’ve experienced if you embrace a constraint rather than ignore it, you will quickly re-prioritize the tasks and find innovative work-arounds for the largest tasks. We do this all the time when working on other projects, the trick is keep focused when working on your own projects.</p> <p>Take this <a href="http://connectionrequired.com">website</a> for example. I went from PSD to Launched in 13 days (17 if you count the weekends). If this was a client project, that would be utterly horrible for this small site. However in the My-Project Timezone, that’s not too shabby. Don’t get me wrong, this site isn’t perfect, instead it’s littered with warts (none of which I’m going to point out). I didn’t focus on building the perfect site, I focused on <strong>good enough for launch</strong>.</p> <h2 id="pick-a-deadline">Pick a Deadline</h2> <p>After launching a few projects, I’ve learned a few useful tips. Pick a deadline and keep your feet to the fire. If it doesn’t look like you can launch by that deadline, rip out features. One large wart for this site, is a missing portfolio section. That was a very intentional omission, one I have designs and even HTML/CSS for, but it was cut in favor of launching. If you’re not a fan of deadlines, set a fake budget and track your time against it. That may even provide a more realistic picture on how you much effort you put into the project.</p> <h2 id="polish-after-launch">Polish After Launch</h2> <p>Another tip I’ve learned: polish after launch. We can become consumed with polishing and tweaking to the point that it holds us back. One advantage our projects have over client projects, is that there’s nothing stopping constant polish after launch. If it’s the critical eye of peers and competitors forcing you to make it perfect, stop and relax. Making a good first impression can drive us insane. It’s important to remember projects don’t stop at launch, they evolve over time. Get it to “good enough”, launch it, then continue polishing.</p> <h2 id="solve-simpler-problems">Solve Simpler Problems</h2> <p>Finally, there are always going to be implementation issues that will get in our way. Our first reaction to figure them out, and that is great, but don’t get hung up on it. It’s okay to change course, alter the final solution, or even post-pone troubling issues. You’re going to be forced to adapt, and you can either take the timeline-hit in figuring out the original problem, or you can change the problem and solve a simpler one.</p> <p>I’m not advocating shoddy or half-assed work, I’m pointing out that launching is more important than perfection.</p> New Traverse City User Group: Northern Michigan Web and Mobile Meetup 2010-10-15T09:48:05-04:00 /blog/2010/10/new-traverse-city-user-group-northern-michigan-web-and-mobile-meetup Zach Moazeni zach.moazeni@gmail.com <p>After we moved back home in Elk Rapids Michigan, one thing that I acutely missed was the number of user groups we had in West Michigan. Don’t get me wrong, West Michigan doesn’t have nearly as many compared to a metropolitan hub. In Chicago they even have a <a href="http://groups.google.com/group/vimchicago">VIM Usergroup</a>. But West Michigan did a lot of events going on, and active communities around them.</p> <p>Instead of lamenting about what’s missing, I decided to put together my own user group up here. Enter the <a href="http://www.meetup.com/nmich-web-mobile/">Northern Michigan Web and Mobile Meetup</a> which will have it’s first event in two weeks at <a href="http://www.meetup.com/nmich-web-mobile/calendar/15108620/">6:00 PM October 28th</a> at <a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=citymac+in+traverse+city&amp;sll=37.0625,-95.677068&amp;sspn=56.59387,91.230469&amp;ie=UTF8&amp;hq=citymac&amp;hnear=Traverse+City,+Grand+Traverse,+Michigan&amp;z=13&amp;iwloc=A">CityMac</a>.</p> <p>I have ideas on the direction and the topics this group could discuss, however I’m sure after a few months we’ll hit a groove and discover what the group’s focus is going to be.</p> <p>So if you’re in the Traverse City area on the 28th and feel like talking geeky feel free to drop by! Make it a point to seek me out and introduce yourself, I love meeting fellow tech geeks.</p> New Company, New Look 2010-10-14T07:37:13-04:00 /blog/2010/10/new-company-new-look Zach Moazeni zach.moazeni@gmail.com <p>I’ve blogged at Simple Chatter for a few years, but now it feels like a good time to retire that domain and roll it into a newer fresher look. Oh and while we’re at it, why don’t we create a new company?</p> <p>World, meet Connection Required; Connection Required, meet the World!</p> <p>This site will continue to be fleshed out in the coming weeks, but feel free to take a gander. And if you have a Ruby/Rails or iOS project in mind, feel free to <a href="/contact">drop me a line</a>.</p> <p>The <a href="http://www.visualhero.com/">visual hero crew</a> did a great job on the design.</p> I think Twitter's OAuth-only Access is a Waste of Time 2010-09-03T15:41:57-04:00 /blog/2010/09/i-think-twitters-oauth-only-access-is-a-waste-of-time Zach Moazeni zach.moazeni@gmail.com <p>I don’t normally write sensationalist articles, but today I’m going depart from the norm and make a bold claim: I think Twitter’s <a href="http://blog.twitter.com/2010/08/twitter-applications-and-oauth.html">change to OAuth-only authentication</a> is a waste of time for developers and businesses.</p> <p>Don’t get me wrong, I think the <a href="http://oauth.net/core/1.0/">OAuth spec</a> is slick and has very good intentions at it’s core. Nor am I knocking it out of inexperience, as I’ve been building OAuth based applications for the past 6 months. However my primary gripe with Twitter’s OAuth-only stance boils down to <a href="http://dev.twitter.com/pages/xauth">xAuth</a>.</p> <p>For those of you that aren’t familiar with Twitter’s recent change, before September 1st you could access their API by passing across your twitter username and password. This was fine if you were using your own credentials, however the Twitter ecosystem is rather large, and most people who interact with Twitter do it via a <a href="http://itunes.apple.com/us/app/twitter/id333903271?mt=8">3rd</a> <a href="http://iconfactory.com/software/twitterrific">party</a> <a href="http://cotweet.com/">service</a>. The security issue is handing your password to an untrusted source. As of September 1st however, developers are only allowed to access the API using <a href="http://dev.twitter.com/pages/auth_overview">OAuth</a>, which requires you to give your username and password <em>only</em> to Twitter and then Twitter will authorize the application on your behalf. Even I admit, the protocol is rather elegant. When it comes to Web applications, I can hardly argue the security. The user is already in a browser and being directed to Twitter and back mostly only fires up User Experience guys.</p> <p>However, I have a huge issue with <a href="http://dev.twitter.com/pages/xauth">xAuth</a>. xAuth is Twitter’s solution to applications that have a large number of passwords already stored and allows the developers to convert them to OAuth-friendly keys behind the scenes. Well, from what I gather, that was the intended purpose. However there are many mobile Twitter applications out there <em>only</em> are using xAuth. Why does this tick me off?</p> <p>1) As a user, I’m still giving my password to the application. The application is not <em>supposed</em> to store the credentials, however this is un-enforceable. At this point I’m only safe-guarded against applications that have their passwords stolen. I won’t deny that’s an issue, but not enough to force all developers to go OAuth-only.</p> <p>2) Even <a href="http://itunes.apple.com/us/app/twitter/id333903271?mt=8">Twitter’s official client</a> requires me to hand over my password. If Twitter is going to take the stance that all API usage is going to go through OAuth, I think they should eat their own dog food and redirect me to a mobile browser.</p> <p>Do I dislike OAuth? Certainly not. And I know Twitter has some brilliant talent. But if Twitter is going to make OAuth vs Basic Auth this big of a priority, I think they are missing mark by moving a lot of applications to xAuth.</p> <p>–</p> <p>You can also discuss this further on <a href="http://news.ycombinator.com/item?id=1660851">Hacker News</a></p> A Public Comment To Those Enabling Abuse 2010-08-31T10:01:16-04:00 /blog/2010/08/a-public-comment-to-those-enabling-abuse Zach Moazeni zach.moazeni@gmail.com <p>Michelle Greer <a href="http://www.michellesblog.net/blogs/my-challenge-to-michael-arrington-techcrunch-hint-its-not-hard">recently wrote about viscous online comments on TechCrunch</a>. Unfortunately, over the years I’ve seen a few ugly incidents where a female was singled out online and verbally attacked. To me, there is one thing worse than these gut-less commenters: it’s telling a minority to “suck it up” or “don’t take it personal” or “that’s just the way it is” or “quit whining”.</p> <p>If you’re someone who tells a minority to ignore abuse, you need to seriously reconsider your position. Because it is sickening.</p> You're running a marathon 2010-08-30T15:50:28-04:00 /blog/2010/08/running-a-marathon Zach Moazeni zach.moazeni@gmail.com <p>I’ll be the first to admit it: I can work myself to death. Paul Boag recently wrote a great article about the <a href="http://boagworld.com/random/work-less">false “badge of honor”</a> regarding long hours in the tech field. I love what I do, and I am continuously grateful that I am able to make a decent living in software. But I’m also prone to burnout.</p> <p>His article really hits home for me because I’ve been burnt out a couple times in the past. I’m still trying to improve my self-awareness, but unfortunately burnout is like bad breath: generally the people around you notice first. So far my main red flag has been “Do I feel like I’m sprinting from task to task?” Despite what some startups believe, running a company is a marathon.</p> <p>Over the years, I’ve been approached by numerous people asking how they can break into software. How hard is it? How smart do you have to be? Which language is best? My main answer is always: to succeed in software you have to enjoy what you do and you have to be persistent. Software changes monthly, and sometimes it feels daily. At first enthusiasm will carry you through long hours reading and playing with new libraries and frameworks. But when you start picking up your 5th language and your 15th web framework, your motivation is primarily going to come from enjoyment. The key is realize you’re not sprinting to an finish line. You’re running a life-long marathon. I think the same philosophy applies to succeeding in business.</p> <p><em>I almost edited the previous paragraph to say “career-long”, yet when I quantify how many hours of my life will be spent earning a living, labeling it “career-“ just doesn’t seem appropriate.</em></p> <p>There is nothing wrong with hard work and I am certainly not promoting laziness. However over-committing yourself or coming home unhappy is a recipe for disaster. Make yourself take enjoyment from your work and from outside of work. And if you can’t, I wish for you the courage to make the necessary changes.</p> Client Satisfaction and Resources 2010-08-24T10:47:20-04:00 /blog/2010/08/client-satisfaction-and-resources Zach Moazeni zach.moazeni@gmail.com <p>I’ve heard the argument “it takes Resources to provide good Client Satisfaction” and I simply have to disagree. <em>Great</em> client satisfaction has to come first, and the resources will follow. I also believe that companies who take this stance will often re-use those resources to provide even better client satisfaction.</p> <h2 id="bargains">Bargains</h2> <p>When most people think about making their clients happy, they think about bargains. A bargain is simply the same services at a reduced price. And that certainly is one factor in making clients happy, since who doesn’t like saving money? Sadly, I think bargains are the first to come mind because they are so easy compared to the alternatives. “I’ll just slash my prices and more clients will come.” However there are other ways to provide value and make clients happier without taking a hit on price.</p> <h2 id="craftsmanship">Craftsmanship</h2> <p>Some clients heavily prefer the quality of the service or product. If I’m in the market for a high-end acoustical guitar, I’m going to favor the quality of a guitar over the one with the best deal. This resonates strongly with software in the form design and often overlooked practices such as automated testing.</p> <h2 id="listening-and-solving-the-real-problem">Listening and Solving the Real Problem</h2> <p>One of the most effective strategies I’ve found is: listen to the client and focus on solving the problem at hand. When I say “listen”, I don’t mean “hear the sounds coming from their mouth”, I mean <em>really</em> listen to both what they are saying, and what they aren’t. There are many clients out there who will pass on a bargain if it only partially solves their problem. I constantly hear stories from both other developers and clients about scenarios where developers ignored the problem at hand, and decided to solve a different one. They may work on an optimization issue when performance is acceptable, or decide to roll the work into an open source project when a specific and custom solution is really what’s needed. Or build an exquisitely elegant design, that even I admire, yet it does not do what is requested.</p> <p>This is not just applied to software. A friend of mine owns a couple local pizza and ice-cream parlors and he shared his experience: The best way to help an upset customer is simply to listen to them. Most of the time they’re not wanting their money back or a coupon. They just want to know that their voice was heard and respected.</p> <p>I always laugh at how appropriate this SNL clip is:</p> <object width="512" height="288"><param name="movie" value="http://www.hulu.com/embed/8K3jmsS5ay9KB3yX06Q17Q" />&lt;/param&gt;<param name="allowFullScreen" value="true" />&lt;/param&gt;<embed src="http://www.hulu.com/embed/8K3jmsS5ay9KB3yX06Q17Q" type="application/x-shockwave-flash" width="512" height="288" allowfullscreen="true" />&lt;/embed&gt;</object> <h2 id="experience">Experience</h2> <p>Sometimes clients just want a memory to cherish. A couple years back, my wife and I decided to go to a <a href="http://www.ruthschris.com/">Ruth’s Chris Steak House</a> and we were amazed at the dining experience. It was certainly one we won’t forget. When we were in Portland, we also had a fantastic night at <a href="http://www.portlandcitygrill.com">the City Grill on the 30th floor</a>.</p> <p>In software, that’s exactly the purpose field user experience design. Certain applications thrive by continuing to improve their interface to be as simple and effective as possible. Other applications thrive by removing options and choices from their customer base. A recent post by <a href="http://www.dilbert.com/blog/entry/the_less_feature/">Scott Adams specifically addresses this point</a>.</p> <p>None of these points take significant resources to do well, and each of these can be improved with additional resources. Please share any successes or failures you’ve found. I’d love to hear them.</p> <p>–</p> <p><a href="http://news.ycombinator.com/item?id=1629929">You can also discuss this post on Hacker News</a></p> Clients that Care 2010-08-03T11:08:25-04:00 /blog/2010/08/clients-that-care Zach Moazeni zach.moazeni@gmail.com <p>Whenever a client hires me, I make it a very specific point not to bring baggage to the table. Whether it’s other project scheduling issues, family schedules, strong toolset opinions, or any other demands on a freelancer’s time or mental-space. Some clients will gracefully deal with a freelancer’s baggage, but I’ve found that most clients would rather not have to concern themselves with it. As a result I work hard to guard my clients from that stress.</p> <p>However, some baggage does inevitably come through. During the middle of July, we had a family emergency and I needed to immediately take off a couple weeks. Keep in mind two of the projects I was on were planning on launching at the end of July. So going offline that abruptly was a significant disruption.</p> <p>I talked with each of my clients individually and I was relieved that they all took it well. After my first week out though, I was shocked at how thoughtful some of the emails/IMs/and Phone calls were. Here are a few snippets that I received:</p> <blockquote> <p>“Just wanted to check in and make sure you were doing ok. Again, let us know if there’s anything we can do.”</p> </blockquote> <blockquote> <p>“Obviously, do what you need to do for yourself. If that’s work I’ll be happy to keep you busy… We’d like to send some flowers…” (In response to me mentioning I may work on a feature to keep my mind busy)</p> </blockquote> <blockquote> <p>“Take care and as much time as you need.”</p> </blockquote> <blockquote> <p>“Please take the time you need.”</p> </blockquote> <blockquote> <p>“Nothing comes before family. Take your time, when you’re ready to start back up just let me know. No rush.”</p> </blockquote> <p>I’ve come to appreciate that my ideal client is not just a cool project nor a large budget, but instead a rich relationship. I feel very blessed that all of my clients are so considerate because there are many others out there who aren’t.</p> <p>Thank you Mike, Dana, Orlando, McHughson, Daniel, and Nicholas.</p> Why should I do business with you? 2010-06-17T08:58:36-04:00 /blog/2010/06/why-should-i-do-business-with-you Zach Moazeni zach.moazeni@gmail.com <blockquote> <p>All things being equal, people want to do business with their friends.</p> <p>All things being not quite equal, people STILL want to do business with their friends.</p> </blockquote> <p><em>- Jeffrey Gitomer in <a href="http://www.amazon.com/Little-Red-Book-Selling-Principles/dp/1885167601">The Little Red Book of Selling</a></em></p> <p>I love this quote and <a href="http://www.amazon.com/Little-Red-Book-Selling-Principles/dp/1885167601">this book</a>. <a href="http://opensoul.org/">Brandon Keepers</a> posed a question over a couple beers (well OK, maybe more than a couple beers):</p> <blockquote> <p>If a client asks you: “Why should I do business with you?”, how would you answer?</p> </blockquote> <p>It was a great question. I took a long pull of my <a href="http://newhollandbrew.com/corp/beer">Poet</a> and stammered an answer. After thinking about the question a couple days, here’s my (sober) answer “because I’m honest, I work very hard, and I have a blast doing what I do”.</p> <p>Here’s the longer version:</p> <h2 id="differentiators">Differentiators</h2> <p>I consider the following as ways for a company to set themselves apart from their competitors.</p> <p><strong>Cost</strong> : How much does it cost for your services or for your product?</p> <p><strong>Expertise and Experience</strong> : How much experience do you have with the tools you’re working with and the problems you’re working on? Are you considered a leader in your community?</p> <p><strong>Influence</strong> : Do you have an audience that your clients also wants to connect with? Do you have certain investment or new business contacts that your clients want you to make introductions?</p> <h2 id="fundamentals">Fundamentals</h2> <p>These are the fundamentals I feel successful businesses have to address if they want to provide value for their clients.</p> <p><strong>Authenticity</strong> : Are you honest are you about what you’re selling and your ability to meet certain commitments?</p> <p><strong>Hard Work</strong> : Do you care about what you’re doing and more importantly who you’re doing it for? What is the quality of your work? How often do you put yourself in your client’s shoes to better serve their needs?</p> <p><strong>A Great Attitude</strong> : Are you fun to work with? Do your clients dread picking up the phone or having you drop by or do they walk away with a better attitude? Do you make your clients feel ignorant when they ask questions? Do you listen to feedback well?</p> <h2 id="mixed-priorities">Mixed Priorities</h2> <p>I see many companies focusing on ways to differentiate themselves and ignore the fundamentals at the same time. Don’t get me wrong, I think the differentiators are great, but only when they build from the fundamentals.</p> <p>I’m a reasonably smart guy, and I’ve been around the block many times building applications using Ruby/Rails. But my experience, my network, and even my cost are all secondary to providing <strong>value</strong> through hard work and honesty. As well as being able to do it in a way that makes it a lot of fun for both my clients and myself.</p> <h2 id="i-want-to-hear-from-you">I want to hear from you</h2> <p>What I love about this outlook is that there are no barriers to the doing the fundamentals well. They are difficult to do well, hell I’m constantly learning new ways to better apply them, but you can start immediately. Your experience, expertise and network are all built over time.</p> <p>What has been your experience? I want to hear from both companies who provide their services as well as from clients who have had great and less-than-great interactions.</p> Nginx is extremely performant 2010-06-11T13:10:16-04:00 /blog/2010/06/nginx-is-extremely-performant Zach Moazeni zach.moazeni@gmail.com <p>A couple days ago I wrote an article about <a href="/2010/06/i-am-far-from-unemployed">perceptions I’ve encountered as a freelancer</a>. The article took off on <a href="http://news.ycombinator.com/">Hacker News</a>, which a great feeling.</p> <p>I received several thousand page views in the process. At first I was like “Wow!” and that quickly turned into “Oh crap…” I wasn’t sure if my server hosting this blog would be able to handle that amount of traffic. But <a href="http://nginx.org/">Nginx</a> handled it like a champ.</p> <p>When I setup this blog, I found an excellent How-To article on setting up Wordpress on Nginx using FastCGI from <a href="http://elasticdog.com/2008/02/howto-install-wordpress-on-nginx/">Aaron Schaefer</a>. At the time this blog was using 5 PHP CGI processes and wasn’t using any form of Wordpress page caching, however neither memory nor CPU broke a sweat. And I’m only running on a few hundred MBs.</p> <p>I don’t think Nginx is a perfect fit in every instance, however I’m really pleased with the results. I definitely recommend it to anyone else who is on the fence.</p> I am far from unemployed 2010-06-09T08:13:55-04:00 /blog/2010/06/i-am-far-from-unemployed Zach Moazeni zach.moazeni@gmail.com <p>Monday was the beginning of my third week working independent. And these three weeks have been a lot of fun. Very busy, but fun. However one thing that surprised me was the reaction from others when they asked what I do for a living.</p> <p>The most typical reaction I have been getting from local friends and acquaintances who are full time employees is sympathy. Phrases like “Well you gotta do what you gotta do.” or “Things will turn around, the economy is improving.”</p> <p>No no <strong>NO!</strong></p> <p>I <em>chose</em> this path. I wasn’t forced to go independent as a result of a layoff or the inability to find full time employment. I love what I do and I love that I am able to help other businesses who need my skills and experience.</p> <p>This reminds me of a blog post a friend shared years ago, that has now been lost, which described an Entrepreneur’s move from the Midwest to the West Coast. He too was taken back at the differences in reactions. Generally a Midwest-ern would say something out of pity or look at him as if he were jobless. While the people he talked to in the West Coast would get excited and share their own dreams of starting something new.</p> <p>I am far from unemployed and so are the plethora of other software freelancers out there. Sure we may have downtime, but that is just one part of our world. So the next time you are talking to someone who mentions they are independent, and you feel sorry for them, mentally check yourself. If you like the guy or gal, ask for their business card and pass it to someone you think would need their services or tweet/blog about the great interaction you had with them.</p> <p>I appreciate that way more than sympathetic encouragement.</p> <h3 id="update-6122010">Update 6/12/2010</h3> <p>This article took off on Hacker news and there was a lot of <a href="http://news.ycombinator.com/item?id=1416883">great feedback</a> both for and against this post.</p> Big Changes. Going Independent. 2010-05-24T13:30:12-04:00 /blog/2010/05/big-changes-going-independent Zach Moazeni zach.moazeni@gmail.com <h2 id="whats-the-news">What’s the news?</h2> <p>Starting this week, I’m putting out my shingle … I’m going independent … and I’m praying I won’t starve.</p> <p>I feel blessed with the opportunities I’ve encountered along my journey, and lately I have had a strong urge to strike out on my own. I want to experience what it’s like to be an independent business owner, and I am excited for the opportunity to work with extremely smart developers as I continue this path.</p> <p>I’m also curious if I will come to love this new freedom or if I’ll hang myself with it.</p> <h2 id="but-why-are-you-leaving-elevator-up">But why are you leaving Elevator Up?</h2> <p>Elevator Up has been such a great experience. Both Aaron and Dayna are great guys and they truly care about both their employees and their clients.</p> <p>One thing I really appreciate about my experience at Elevator Up has been my exposure to many different communities and business models. Even though I’m going independent, I don’t feel like I’m leaving Elevator Up, but instead that I’m staying focused on the development community to which I feel the most attached.</p> <p>This transition allows me to stay specialized in the my sphere while at the same time still being able to work with the gang at Elevator Up in areas where my strengths shine. It also allows me to engage with clients that may not have originally been a good fit for Elevator Up, but then allow me to bring Elevator Up into the conversation as they grow.</p> <p>Aaron once said the phrase “Just because you work independent, doesn’t meant you work alone” and I’m pumped that I am continuing my relationship with Elevator Up, but in just a different light.</p> <h2 id="ok-what-do-you-really-think-about-elevator-up">Ok, what do you <em>really</em> think about Elevator Up?</h2> <p>Just that, Elevator Up is a great place to work and a great family. I think the people who know me best know that I’m too honest to say it any other way. And frankly, I don’t have the energy to put up a false ruse.</p> <p>I’m excited for this next adventure, and I’m grateful for the support of my Wife, my friends, and especially my family at Elevator Up.</p> <p>If you want to talk more about my transition or have any Ruby/Rails projects feel free to <a href="/about">read about my experience</a> and send me an email at zach -dot- moazeni -at- gmail -dot- com.</p> Great perspective on incentive and value 2010-05-20T12:08:03-04:00 /blog/2010/05/great-perspective-on-incentive-and-value Zach Moazeni zach.moazeni@gmail.com <blockquote> <p>“Finally, I don’t think there’s much value in arguing over who has the most incentive to create good products. The point is you’ll be better off in the long-term if you delivery high-value high-quality products, regardless of whether you’re a consultancy, startup, or enterprise shop.”</p> </blockquote> <p>– <a href="http://www.patmaddox.com/">Pat Maddox</a> on <a href="http://www.patmaddox.com/2010/05/19/are-you-punching-your-users-in-the-face/">Are you punching your users in the face?</a></p> Review: Crucial Conversations 2010-05-17T21:55:08-04:00 /blog/2010/05/review-crucial-conversations Zach Moazeni zach.moazeni@gmail.com <p>Months ago I shared my difficulties with leading others and sharing opinions effectively with Josh Little from <a href="http://www.bloomfire.com/">Bloomfire</a>. He recommended I pick up the book <a href="http://www.amazon.com/dp/0071401946?tag=tibesti-20">Crucial Conversations</a> and even mentioned he has had to re-read it many times over the years. It was the first book I finished on my vacation, and I am very grateful for his recommendation.</p> <p>After the first few pages, I was initially put off by the number of buzzwords and new vocabulary (e.g. “shared pool of meaning”). I often get turned off with authors who would rather invent vague terminology than give clear meaning. However despite the capitalization, I felt the book provided great insight.</p> <p>Some especially key take-aways for me were:</p> <ol> <li>Reaffirmation to first improve myself especially since it’s much easier than trying to change others.</li> <li>The differences between content and context. I certainly get bogged down with specifically what someone says rather than thinking about their frame of mind. Or what they’re not saying and merely hinting at.</li> <li>Highlighting the connection between open dialogue and a safe environment, with steps on helping set the context in a safe way.</li> <li>The differences between apologizing vs contrasting, and when apologizing is and isn’t appropriate. This book helped me realize that I’ve used apologizing as a social crutch rather than expressing regret over a transgression.</li> <li>I liked the authors opinion on attitude, and how someone else doesn’t change your attitude, you ultimately control your attitude and your actions that result from it.</li> <li>I think the authors did a great job emphasizing the scenarios of how conversations could be handled better. They didn’t just throw out their points for their readers to interpret alone.</li> </ol> <p>And for my biggest take-away: I appreciated that the book focused more on positive, open-minded dialogue and not subversive techniques to manipulate someone else. The points in the book were meant for the contentious and not for the silver-tongued. That just feels right to me.</p> <p>So along with Josh, I wholeheartedly recommend this book. I feel it can help bridge communication break downs between couples and peers alike.</p> My Vacation Reading 2010-05-15T10:41:06-04:00 /blog/2010/05/my-vacation-reading Zach Moazeni zach.moazeni@gmail.com <p>Every time I go on an extended vacation I end up ripping through a half dozen books. It’s probably because of a need to catch up on long overdue reading. Though I have noticed that it mentally refreshes me much more than watching episodes of <a href="http://en.wikipedia.org/wiki/Lost_(TV_series)">LOST</a>.</p> <p>Here was this vacation’s reading list. I’m planning on writing up my summary for each of the books which were all good in their own way.</p> <p><a href="/uploads/2010/05/IMG_0489.jpg"><img src="/uploads/2010/05/IMG_0489.jpg" alt="Crucial Confrontations, Book Yourself Solid, The Go-Giver, The Little Gold Book of Yes! Attitude, The Little Red Book of Selling" title="Vacation Reading List" width="500" class="aligncenter size-full wp-image-291" /></a></p> Muscle Memory 2010-04-14T21:48:01-04:00 /blog/2010/04/muscle-memory Zach Moazeni zach.moazeni@gmail.com <p>Two years ago, I made the commitment to switch the <a href="http://en.wikipedia.org/wiki/Dvorak_Simplified_Keyboard">Dvorak Layout</a> and I think my wrists thank me. When I first started, I heard a lot of concern from others that learning Dvorak would harm their <a href="http://en.wikipedia.org/wiki/QWERTY">Qwerty</a> typing. But I dismissed it as lazy FUD.</p> <p>After two years, I’m admitting I was wrong.</p> <h2 id="why-do-i-still-use-qwerty">Why do I still use Qwerty</h2> <p>I vastly prefer working in Dvorak, however there are plenty of scenarios when I need to work in Qwerty. Pair-programming and directly administering servers have been two recurring situations that force me to consistently finger-stutter.</p> <h2 id="what-is-it-like">What is it like?</h2> <p>I didn’t forget Qwerty, but after working in Dvorak so long I have to take pauses when I switch. Or I’ll rip through a word, only to realize I typed it in the other layout. Other annoyances pop out such as Copy/Paste, Mac Preferences (Dvorak uses the <code class="highlighter-rouge">W</code> for <code class="highlighter-rouge">,</code> which keeps closing windows), and Mac switching tabs (Dvorak uses <code class="highlighter-rouge">-</code> and <code class="highlighter-rouge">+</code> for <code class="highlighter-rouge">[</code>, and <code class="highlighter-rouge">]</code> which keeps changing the font of Textmate/Terminal/Browsers).</p> <p>A different way of explaining these finger-stutters: I like to joke around when someone asks me a VIM command. I tell them “I don’t know, but my fingers do”, and I typically will touch a keyboard, do the command they asked, then tell them the command.</p> <h2 id="what-ive-been-doing-to-fix-this">What I’ve been doing to fix this</h2> <p>So instead of using Dvorak exclusively, then jumping into Qwerty when pairing I’ve resorted to picking a layout at the beginning of the day and working with it the entire day. This has helped keep my muscle memory in shape.</p> <p>I still have problems when switching in and out of Dvorak and Qwerty (e.g. one hour in one layout, then the next hour in the other layout). Perhaps over time I’ll be able to easily switch between the two, but I’m not there yet.</p> <p>If I notice I am hammering the DELETE key, I’ll open up text pad and rip though a few <a href="http://en.wikipedia.org/wiki/Pangram">Panagrams</a>. At first I used the common “The quick brown fox…”, but then I realized my fingers were committing that sentence to memory. So now I’ll open up a site with <a href="http://infinityandbeyond0.tripod.com/id15.html">a list of panagrams</a> and randomly select a few to type out.</p> <h2 id="has-learning-dvorak-been-worth-it">Has learning Dvorak been worth it?</h2> <p>This is hard to answer, but I think so. Like I said I do vastly prefer the Dvorak layout, but I do think it’s important to stay realistic that you’ll have to occasionally use Qwerty. However maintaining muscle memory has been a lot more work than I originally guessed.</p> Harvested: A New Ruby API Wrapper 2010-04-11T14:17:02-04:00 /blog/2010/04/harvested-a-new-ruby-api-wrapper Zach Moazeni zach.moazeni@gmail.com <p>I’ve used <a href="http://www.getharvest.com/">Harvest Time Tracking</a> for well over 3 years, and they have a quality product. They have also had a published API for quite some time. The past couple weeks I’ve been working on Ruby API wrapper for it, and today I’ve pushed the first release.</p> <h2 id="harvested">Harvested</h2> <p>Harvested is a thin wrapper around their API based on HTTParty and HappyMapper.</p> <p>You can go grab and install it through RubyGems (make sure the Gemcutter source is set):</p> <div class="highlighter-rouge"><pre class="highlight"><code> gem install harvested </code></pre> </div> <p>A couple quick examples of how to use this API:</p> <div class="highlighter-rouge"><pre class="highlight"><code>require "harvested" harvest = Harvest.hardy_client('yoursubdomain', 'yourusername', 'userpassword', :ssl =&gt; false) # Print out all users, clients, and projects on the account puts "Users:" harvest.users.all.each {|u| p u } puts "Clients:" harvest.clients.all.each {|c| p c } puts "Projects:" harvest.projects.all.each {|project| p project } </code></pre> </div> <p>More examples are included in <code class="highlighter-rouge">/examples</code> along with the gem.</p> <h2 id="standard-client-vs-hardy-client">Standard Client vs Hardy Client</h2> <p>The guys at Harvest built a great API, but there are always dangers in writing code that depends on an API. For example, HTTP Timeouts, Occasional Bad Gateways, and Rate Limiting issues need to be accounted for.</p> <p>There are two clients available in the gem, <code class="highlighter-rouge">client</code> and <code class="highlighter-rouge">hardy_client</code>. If you want to deal with the issues manually, feel free to use <code class="highlighter-rouge">client</code> otherwise <code class="highlighter-rouge">hardy_client</code> provides a friendly wrapper that retries errors and waits for rate limit resets.</p> <h2 id="links">Links</h2> <ul> <li><a href="http://rdoc.info/projects/zmoazeni/harvested">Harvested Docs</a></li> <li><a href="http://rubygems.org/gems/harvested">Gem on Gemcutter</a></li> <li><a href="http://github.com/zmoazeni/harvested">Source Code on Github</a></li> <li><a href="http://www.getharvest.com/api">Harvest API Docs</a></li> <li><a href="http://groups.google.com/group/harvested">Mailing List for Harvested</a></li> </ul> <h2 id="update---4122010">Update - 4/12/2010</h2> <p>I started documenting the codebase, so I added a link to the <a href="http://rdoc.info">rdoc.info</a> documentation.</p> Reflections of an Adjunct Professor 2010-04-06T22:51:59-04:00 /blog/2010/04/reflections-of-an-adjunct-professor Zach Moazeni zach.moazeni@gmail.com <p>This past semester I had the opportunity to teach the Advanced Java course at <a href="http://www.grcc.edu/">GRCC</a>. Teaching at the college level has been something I’ve wanted to experience for a while and it is also quite aligned with <a href="/vision-statement">my values</a>. While I was excited to start the semester, I was a bit nervous at how my first class would turn out. As the semester progressed, I kept a list of failures and successes that would help me grow as a teacher. I hope this list helps other instructors as they start their career.</p> <h3 id="using-different-tools">Using Different Tools</h3> <p>One decision I made early on was that I would never use PowerPoint. I think the static nature of PowerPoint slides completely disengage students and detract from the learning experience. I was lucky to be introduced to <a href="http://prezi.com/">Prezi</a> months prior from a friend, and at the time I knew it would be a great alternative to boring slides.</p> <p>For instructors that haven’t created a screencast before, I strongly recommend investigating them. I used <a href="http://www.ambrosiasw.com/utilities/snapzprox/">SnapXPro</a> and <a href="http://vimeo.com/">Vimeo</a>, but there are many other alternates out there.</p> <p>Google Docs was a great aid in creating Labs and Surveys. Legos were excellent in illustrating concurrency, especially regarding context-switching. Omnigraffle and Apple Screen Shots were also a big boon when creating my lectures.</p> <h3 id="time-spent-preparing-for-classes">Time Spent Preparing for Classes</h3> <p>I was repeatedly warned by seasoned instructors that the first class is the hardest. But I was completely taken back at how much time I would spend preparing for lectures. The course I taught didn’t have a prior curriculum, and it went against my nature just to teach directly from a textbook. Yet I spent around 5-6 hours a week just putting together the lecture and the accompanying lab.</p> <p>A lot of time was spent on planning the goals of the lectures, preparing them on <a href="http://prezi.com/">Prezi</a>, creating the labs, and grading the previous week’s labs. I worked hard to craft labs that didn’t just evaluate whether the student understood the concept, but supplemented the learning experience from the lecture.</p> <p>I approximate I’ve spent on average 12 hours a week to prepare for a one night-a-week course.</p> <h3 id="going-over-prepared-material">Going Over Prepared Material</h3> <p>I found early on that not going over the material you’ve prepared can really bite you. It helped tremendously to write the solution code to the labs before I gave them the assignments. There was always something I needed to tweak in terms of problem descriptions. Even though I consider myself a good developer, if I just put together an assignment without fleshing out a solution myself, the resulting code would become tedious and lose the original intent.</p> <p>One requirement as innocuous as ‘Format a collection of strings so the output results in: “string1, string2, string3 and string4.”’ had students spending the majority of their time in the wrong area which in turn clouded the lesson.</p> <p><em>Oddly enough, that requirement worked well when teaching Unit Testing.</em></p> <h3 id="office-hours">Office Hours</h3> <p>I think I was one of the few adjuncts who offered office hours. Working at <a href="http://elevatorup.com">Elevator Up</a> I took advantage of our great space at <a href="http://workthefactory.com">The Factory</a> and posted weekly office hours. Despite the Holland-to-GR commute, office hours were an excellent opportunity to have a regular checkpoint for myself to focus on the class. Every Thursday evening from 5 - 7 I knew I would be working on my class.</p> <p>Some instructors mention that students don’t take advantage of their office hours. I had students show up about 50% of the time. I do think that scheduling can be important though. I had a few students mention that they had another class or had to work during those hours, and for those I was very clear that I would make myself available for scheduling issues.</p> <h3 id="people-fall-asleep">People Fall Asleep</h3> <p>I know students fall asleep in class. I even remember times when I did it. But boy is it a shock to be mid-sentence and spot one student nodding off and see another drooling. I think it took all of my willpower to stay focused on the lecture.</p> <p>To other new teachers, don’t take it as an insult. Evening classes in warm rooms can bring down even the most alert. And mentally prepare yourself, because I was completely taken aback.</p> <h3 id="i-love-kinkos">I love Kinkos</h3> <p>Most college offices have great equipment for making copies of exams and syllabuses, but I sure love Kinkos. For $10, they would take a PDF from a USB thumbdrive, print and staple many copies in less than 15 minutes. I know it sounds spoiled, but $10 for saving that time was well worth it to me.</p> <h3 id="due-dates">Due Dates</h3> <p>I might have been described as a strict teacher in the way I accepted assignments. I had a simple rule:</p> <ul> <li>Full Credit was only awarded if the assignment was turned in before the next lecture started</li> <li>I would accept the assignment late 1 day for Half Credit</li> <li>I would not accept an assignment after 1 day late.</li> </ul> <p>This does come across harsh, but I believe this stance carries it’s own benefits.</p> <ul> <li>It helps teach the importance of time management.</li> <li>I wasn’t bombarded by a slew of 3-week late assignments at the end of the semester.</li> </ul> <p>Additionally, I held myself to the same standard. If I would require my students turn in assignments on time, I would hand them back on time.</p> <p>That being said, I do recommend keeping a pulse on the class. There was one instance where I was flexible and extended the deadline of a project 1 week, but I awarded extra credit for the students who turned in their work by the original deadline.</p> <h3 id="pacing-concepts">Pacing Concepts</h3> <p>Another lesson I learned early on was pacing. My early lectures made some pretty large leaps, and while they seemed fairly logical to me it dawned on me that I needed to slow down.</p> <p>That doesn’t mean my students were slow, or that I went to the extreme to ‘dumb down’ my lectures. However there is a sweet spot between being painstakingly redundant and breezing through concepts. This is still an area that I need to work on.</p> <h3 id="connecting-with-students">Connecting with Students</h3> <p>After realizing that I needed to work on pacing, I stumbled upon one of the biggest take-aways for me as an instructor: Connecting with Strangers. I am leading a group of strangers that I not only want to convey new material to, but I want to connect on a level that draws at that ‘Aha!’ moment for each one of them. That is hard enough with an individual, let alone a classroom.</p> <p>This was a very humbling experience for me and I think one area in which I learned the most from my students. Good teachers connect with their students, they don’t just lecture.</p> <h3 id="final-thoughts">Final Thoughts</h3> <p>I had such a great experience teaching my first semester. I’d like to thank my wife for bearing with me these past four months. Especially, so soon after our newborn. I’d also like to thank my company <a href="http://elevatorup.com">Elevator Up</a> for encouraging me to explore this avenue.</p> <p>I’ve been asked if I will teach another class. If the opportunity presents itself, I may, but I now have more respect for the amount of effort teaching requires. Regardless, I did have fun and I learned more about myself and about communicating with others.</p> <p>I welcome feedback from other teachers and how their early experiences went.</p> Introduction to Rails Talk: GVSU Mobile Developers Group 2010-03-02T20:23:59-05:00 /blog/2010/03/introduction-to-rails-talk-gvsu-mobile-developers-group Zach Moazeni zach.moazeni@gmail.com <p>Here is the Prezi talk I gave to the GVSU Mobile Developer User Group.</p> <div class="prezi-player"><style type="text/css" media="screen">.prezi-player { width: 550px; } .prezi-player-links { text-align: center; }</style><object id="prezi_v0apok8pqf9x" name="prezi_v0apok8pqf9x" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="550" height="400"><param name="movie" value="http://prezi.com/bin/preziloader.swf" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="prezi_id=v0apok8pqf9x&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no" /><embed id="preziEmbed_v0apok8pqf9x" name="preziEmbed_v0apok8pqf9x" src="http://prezi.com/bin/preziloader.swf" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="550" height="400" bgcolor="#ffffff" flashvars="prezi_id=v0apok8pqf9x&amp;lock_to_path=0&amp;color=ffffff&amp;autoplay=no" />&lt;/embed&gt;</object><div class="prezi-player-links"><p>Published via <a href="http://prezi.com">Prezi</a></p></div></div> My First Introduction to Haskell Extensions: FlexibleInstances 2009-07-26T20:28:53-04:00 /blog/2009/07/my-first-introduction-to-haskell-extensions-flexibleinstances Zach Moazeni zach.moazeni@gmail.com <p>I’ve been tinkering with Haskell lately, and I came across an odd issue. Let’s say I want to create a class that takes Integers, Characters, and Strings and returns an Integer.</p> <div class="highlighter-rouge"><pre class="highlight"><code>class Something a where doSomething :: a -&gt; Integer instance Something Integer where doSomething x = 1 instance Something Char where doSomething x = 2 instance Something [Char] where doSomething x = 3 </code></pre> </div> <p>Trying to load this into the interpreter (or compiling it) results in the an error on the <code class="highlighter-rouge">String</code> (<code class="highlighter-rouge">[Char]</code>)</p> <div class="highlighter-rouge"><pre class="highlight"><code>Illegal instance declaration for `Something [Char]' (All instance types must be of the form (T a1 ... an) where a1 ... an are type *variables*, and each type variable appears at most once in the instance head. Use -XFlexibleInstances if you want to disable this.) In the instance declaration for `Something [Char]' </code></pre> </div> <p>This cryptic message essentially means that I can’t make <code class="highlighter-rouge">[Char]</code> an instance of the <code class="highlighter-rouge">Something</code> class, but I could make <code class="highlighter-rouge">[a]</code> one (a list containing any type)</p> <div class="highlighter-rouge"><pre class="highlight"><code>instance Something [a] where doSomething x = 3 </code></pre> </div> <p>However that stinks because then I have to do a lot more work to differentiate between a list of characters (i.e. Strings) and a list of integers.</p> <p>The error message mentions a way to disable that check, and the helpful guys at <a href="http://freenode.net/"><code class="highlighter-rouge">#haskell</code></a> gave me a hand.</p> <p>If you put this at the top of your source file:</p> <div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="err">-#</span><span class="w"> </span><span class="err">LANGUAGE</span><span class="w"> </span><span class="err">FlexibleInstances</span><span class="w"> </span><span class="err">#-</span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p>you end up telling Haskell to load the extension <code class="highlighter-rouge">FlexibleInstances</code> which allows you to differentiate between lists of characters, integers, or whatever else. Which allows you to load in:</p> <div class="highlighter-rouge"><pre class="highlight"><code>-- in file TestingTypes.hs {-# LANGUAGE FlexibleInstances #-} class Something a where doSomething :: a -&gt; Integer instance Something Integer where doSomething x = 1 instance Something Char where doSomething x = 2 instance Something [Char] where doSomething x = 3 -- in ghci: Prelude&gt; :l TestingTypes.hs [1 of 1] Compiling TestingTypes ( TestingTypes.hs, interpreted ) Ok, modules loaded: TestingTypes. *TestingTypes&gt; doSomething 1 1 *TestingTypes&gt; doSomething 'c' 2 *TestingTypes&gt; doSomething "foo" 3 </code></pre> </div> <p>Nothing ground breaking, but I thought I’d pass this tidbit along.</p>