-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathatom.xml
More file actions
1873 lines (1241 loc) · 142 KB
/
atom.xml
File metadata and controls
1873 lines (1241 loc) · 142 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Dan Adams]]></title>
<link href="http://mrdanadams.com/atom.xml" rel="self"/>
<link href="http://mrdanadams.com/"/>
<updated>2015-01-27T21:26:47-05:00</updated>
<id>http://mrdanadams.com/</id>
<author>
<name><![CDATA[Dan Adams]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[How to Work with Consultants Successfully]]></title>
<link href="http://mrdanadams.com/2013/how-to-work-with-consultants-successfully/"/>
<updated>2013-01-01T12:56:00-05:00</updated>
<id>http://mrdanadams.com/2013/how-to-work-with-consultants-successfully</id>
<content type="html"><![CDATA[<p><a href="http://www.toothpastefordinner.com/index.php?date=100210" target="blank" rel="nofollow" title="Toothpaste for Dinner" class="aligncenter"><img src='http://mrdanadams.com/images/consulting-consultant_400x400.gif' /></a></p>
<p>A bit of advice on how to work successfully with a consultant (or consulting firm).</p>
<!-- more -->
<h2>Get an inside look at their portfolio</h2>
<p>Consultancies usually put up a public portfolio. This is the work they want you to see or the work they are allowed to show publicly. You want to see what they’ve done in the last year. This will give you best sense of their capabilities and the direction of the firm. They can often show recent work in private, if asked, that they can’t promote publicly.</p>
<h2>Talk to the people who do the work</h2>
<p>While I’m not a huge fan of actually interviewing every consultant as you would a full time hire (you really shouldn’t have to), talk to people at the firm that actually do the work (i.e. not the sales guy). Have a chat about your project and what it seems like to them. They should not only understand it quickly but be excited about it and have new ideas and suggestions.</p>
<p>Some firms will sell you their A team and give you the B (or C) squad. It’s not that common but it happens. These guys are jerks.</p>
<h2>Keep the owner in the loop</h2>
<p>You have every right as a client to give feedback to the person who cares the most: the owner. If you have a problem or feel like the project team isn’t pulling through get on the phone and make your concerns known in no uncertain terms.</p>
<p>This may sound harsh but it’s the best thing for both you and the firm. They can’t fix problems they don’t know exist. It’s easier to make it work with a firm you’ve already selected than finding another one.</p>
<h2>Go local. Get them in-house.</h2>
<p>The more closely you collaborate and the more often you talk the lower your risk. You’ll more quickly find issues and get things running smoothly. If possible, get a shop in town and have them live with you. They’ll spend less time with communications overhead (and more time on real work) and you can get updates from them directly instead of reading some sort of status report.</p>
<h2>Have an out. Be protected.</h2>
<p>Don’t leave yourself with all the risk <a href="http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants">hiring a consultant</a>. One of the benefits not available with full-time hires is that consultants come with contracts or Statements of Work. This means either resource for hire or fixed bid (see below). Even in the case of team augmentation, encode something in the contract promising at least a minimum set of deliverables.</p>
<p>Don’t pay (everything) up front. Tie payment to deliverables or spread out over the schedule.</p>
<h2>My experience hiring consultants</h2>
<p>I’m a consultant. I’d like to think I’m one of the good ones. But I’ve also played this game from the other side.</p>
<p>I was the Head of Engineering in a product company creating a new product (essentially a startup from the inside). In order to serve both our old client / agency work and our new product development (while having no up-front capital investment) we had to get creative with engineering resources.</p>
<p>We worked with outside firms in Argentina, Connecticut, New York City, India, Vietnam, and elsewhere.</p>
<p>We worked with independent contractors / consultants in just as many places.</p>
<p>Eventually, we even set up an off-shore development team in Omsk, Russia.</p>
<p>In the first two cases (using contractors / consultancies) my success rate was maybe 25%. That’s rough. But once we found a person or shop that worked we set up a long-term relationship with them. In fact, one of the best contractors I ever worked with was just “some dude” out in Ohio with three kids getting his PhD. I never even met him.</p>
<p>These relationships were a valuable asset and complimented our sizable internal team. It allowed our business to react to resource needs without reducing internal staff.</p>
<h2>Appendix: Consulting Pricing Models</h2>
<p>(Is putting an appendix on a blog post a sign it’s too long? Oh, well.)</p>
<p>If you are unclear with how pricing works with a consultant, here’s the gist. Each model has it’s own risk and benefits and one is not clearly better than another.</p>
<h3>Fixed Bid</h3>
<p>You tell them what you want, they ask some questions, make some assumptions, and give you a document telling you what they will do and how much it costs. If you change your mind during the project, you get a change order (changing the contract and probably asking for more money). By signing the contract, you are in for the whole amount (assuming they deliver).</p>
<p>As a client, you get a promise of what they’ll deliver. In many ways the consultant holds the most risk here because if the project goes over budget (and they don’t manage the changes in cost) they are left eating the overage. Since they are holding the cost risk any fixed bid contract will include some degree of risk buffer.</p>
<p>These contracts are appropriate for projects that are fairly well defined and well bounded.</p>
<h3>Staff Augmentation</h3>
<p>You pay a certain rate by the person for as long as you have them. There is no document saying what they will deliver. There is no risk of going over budget for the consultant but the client has no guarantee what will be delivered. However, the client can change their mind every day if they wish. Also, the clients financial obligation is less than a fixed bid contract since the staff augmentation can more or less be stopped at any time.</p>
<p>This model is appropriate when the problem being solved isn’t well defined such as <a href="http://mrdanadams.com/2012/tips-startups-products-off-ground">working with early stage startups</a>.</p>
<h3>Hybrid</h3>
<p>While more of a contract detail, these two models can be combined such that the client gets a bit more guarantee of what will be delivered while at the same time not having to deal with change orders every week.</p>
<p>Identify a few high-level, critical deliverables and encode those in the contract. You can still change direction during the project although, if it’s a big enough change, you might have to do a change order.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Why Your Startup Shouldn't Hire Consultants]]></title>
<link href="http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants/"/>
<updated>2012-12-30T11:13:00-05:00</updated>
<id>http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants</id>
<content type="html"><![CDATA[<p><img src="http://mrdanadams.com/images/dont-worry-consultant.jpg" alt="Don't worry I'm a consultant" /></p>
<p>Hired guns. Code mercenaries. Consultants.</p>
<p>Whether you are early stage or have an established product, bringing a consultant onto your startup can be a dicey prospect. Here are reasons not to and some things you may not have thought of.</p>
<!-- more -->
<p>While there is truth to all of these, there’s more to be considered. Reality is, many startups are <a href="http://mrdanadams.com/2013/how-to-work-with-consultants-successfully/">successfully using consultancies</a> (although they may not advertise it). In a world where good people are hard to hire, time to market is tight, and product quality is critical, hiring a consultant is not always the right thing to do. However, it’s also not always not either (if you’ll pardon the double negative).</p>
<h2>Reason 1: They’re Expensive</h2>
<p>If you need to hire a design / engineering consultant (in Boston, at least) you are likely looking at $100 - $250 per hour depending on the shop and what you need. That’s $4,000 - $10,000 per week per full time person. Assuming you had a full time hire that had a total cost of $150k per year (after taxes, benefits, overhead, etc) that’s around $72 / hour.</p>
<p>Ouch.</p>
<p>Of course, this assumes we are comparing cost for the same value which probably isn’t true. Not only might you be getting something better or different than you have internally but you don’t have the same costs associated. A consultant pays their own taxes, vacations, benefits, etc.</p>
<p>You usually don’t hire a consultant for a whole year. You bring them on for something specialized or because they offer something you don’t have in your internal team or can’t hire. They’ve got experience and skills that can prove to be invaluable:</p>
<ul>
<li><em>Strategic insight</em>. A general knowledge of the industry, best practices, and the ability to pinpoint the best technical or product direction.</li>
<li><em>They’re battle tested</em>. They’ve been through the full project life cycle across different team sizes, contexts, and technologies. This adds crucial perspective that only comes by experience. Especially knowledge that only comes from previous failures.</li>
<li><em>They’re good. Maybe really good.</em> That’s why they are “expensive” (usually). They’ve got the skills you need and can get things done quickly.</li>
<li><em>Consultants come with friends.</em> Hiring a consultant from a consultancy means you’ve got a pool of similar resources you can draw from quickly without going through the hiring process for each one. Even for independent consultants, they often have a wider network to draw from when you need someone based on personal recommendation.</li>
<li>Consultants are easier to let go.</li>
</ul>
<p>You may bring on a consultant simply to augment your team and add momentum. Add a consultant or two where it makes sense, crank for a while, and then scale down to a slower pace after the push.</p>
<p>In a perfect world, when you needed to grow your team you’d be able to hire passionate, invested people with the skills you need when you need them. That’s a fantasy. The reality is that hiring (both salary and consultant) is hard; it takes a lot of your time and involves significant risk.</p>
<h2>Reason 2: They Walk Away with Knowledge</h2>
<p>History matters in a startup. The reasons why things are what they are (both good and bad) are important. A consultant may come in, do some great work, and then walk out with all the good bits in their head. This is a legitimate risk for anyone hiring a consultant.</p>
<p>It’s not, however, a show stopper:</p>
<ul>
<li>Integrate them in the team so there is knowledge sharing during the work. Make sure other people at least know, and ideally have worked on, their stuff. Do code reviews. Seriously, just do them.</li>
<li>Have them put together documentation during the course of the work (not at the last minute) and use it themselves.</li>
<li>Embed them in team discussions so they know why the team did what they did and vice-versa. The random, informal conversations are often the most valuable.</li>
</ul>
<p>If this is a concern, don’t give a consultant the most important piece of your product and then have nobody else know about it. Add some redundancy or minimize risk by putting them on something else.</p>
<h2>Reason 3: They’re Not Invested</h2>
<p>Your core team is passionate and invested, you hope. (Let’s assume they are.) A consultant is usually seen (rightly) as an outsider who comes in, does something, and walks away with no strings attached. The assumption is that they won’t deliver the same quality or be as proactive at addressing problems as your core team.</p>
<p>Again, a legitimate concern.</p>
<p>Get a consultant with a good reputation, a good body of work, and seems like someone you can trust. Protect yourself by getting them as closely integrated with you and your team as possible; tight collaboration will quickly shed light on lurking concerns.</p>
<p>It’s incorrect to assume that consultants don’t have stake in your project. Consultants live and die by their portfolio and having happy clients is critical. Finding new clients is expensive and difficult. Happy clients come back for more work. Especially when working with startups, it’s foolish for me not to do everything I can to help them succeed.</p>
<h2>Allow Me to Illustrate</h2>
<p>Here are a few scenarios where bringing in a consultant may make a lot of sense.</p>
<h3>Get Your Product Off the Ground</h3>
<p>As a founder in an early stage startup, you face many challenges in <a href="http://mrdanadams.com/2012/tips-startups-products-off-ground">getting your product off the ground</a>. Furthermore, many founders have never designed a product, built a software platform from the ground up, hired a creative team, scaled anything, worked with big data, and might be working with emerging technologies such as mobile.</p>
<p>That’s a big, steaming pile of risk. Building the wrong thing, building it poorly, or taking too long can be a killer.</p>
<p>Bring in a (very) small team of experienced consultants who can not only build the technology but help you understand the product, the technical landscape, and build the right product. Furthermore, while working with the consultant you can be working on hiring that long-term team. The product gets built in the time needed and you get some breathing room to put your team in place.</p>
<h3>Add Momentum or Features to an Existing Product</h3>
<p>You’ve got your core team established and product off the ground. You have a well defined roadmap. Problem is, you simply don’t have the man power to get it all done in the time needed.</p>
<p>Bring in a handful of consultants, integrated them tightly into your team, and hit the ground running.</p>
<h3>Bring in Some Expertise</h3>
<p>This is the likely the most common case for a consultant; you need a special skill you don’t have in-house and doesn’t make sense to develop. Find a consultant that can do it but can also do a lot more; integrate them as much as possible and get as much out of them as you can. For instance, if you bring in a firm to do some mobile work get them to add value in other places such as touching the REST APIs, helping you create a mobile roadmap for after they are gone, or giving advice on to what degree your web apps need to be responsive.</p>
<h2>Consultant != Contractor</h2>
<p>I’m making an assumption here in the difference between a consultant and a contractor (this is my definition). Consultants (disclaimer: like me) are often brought on for their thoughts and experience in addition to getting the work done. Consultants may even challenge their clients with new ideas and approaches.</p>
<p>Contractors are brought on to perform a task, build a feature, do something specific.</p>
<h2>Success or Horror?</h2>
<p>Have you considered using a consultant at your startup? If you’ve used one, how was your experience?</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[5 Tips for Getting Your Startups Product Off The Ground]]></title>
<link href="http://mrdanadams.com/2012/tips-startups-products-off-ground/"/>
<updated>2012-12-28T21:27:00-05:00</updated>
<id>http://mrdanadams.com/2012/tips-startups-products-off-ground</id>
<content type="html"><![CDATA[<p><img src="http://mrdanadams.com/images/getting-your-startup-off-the-ground.jpg" alt="Lift off getting your startup product off the ground" /></p>
<p>For most of 2012, I was embedded in <a href="http://dogpatchlabs.com">Dogpatch Labs</a> working with early stage startups, such as <a href="http://www.bytelight.com/" title="Indoor Location">ByteLight</a>, to create the core product and get them to the next level. All faced the challenges of a pre-MVP product and small team. Here’s some of what’s involved, the challenges faced, and approaches to tackle them.</p>
<!-- more -->
<h2>#1 Establish Vision, Create Focus</h2>
<blockquote><p>What defines success?</p></blockquote>
<p>The answer to that question will change. Possibly often. You should ask it regularly and make sure your whole team understands the answer and the reasoning behind it. The answer will likely change based on how far out you look: what defines success for the next month will be different than 3 months or 12.</p>
<p>You may struggle to identify a single definition of success but it’s important that you do so (or attempt to as best as possible). Back up the goal with ways to measure it. Benchmark team progress regularly against it.</p>
<p>Give your team a clear sense of why you are there, how you are going to take over the world, and that they are expected to be a big part of that.</p>
<p>Some examples:</p>
<ul>
<li>Being customer ready (if you sell a customer you can actually support them)</li>
<li>A product that demonstrates your direction strongly enough to incite new interest from press and investors</li>
<li>Getting enough app usage to demonstrate that, yes, the market really is there</li>
<li>Getting the “hockey stick”: non-linear growth in app usage, downloads, conversions, sales, or whatever else is the most important success metric for your app.</li>
<li>Generating enough revenue to get you bootstrapped</li>
</ul>
<h2>#2 Continuously Re-Define Your Product</h2>
<p>While keeping your eye on what defines success, re-evaluate your product design. Evolve. Tweak. Change. Nothing is sacred. Get as much feedback and input from press, investors, peers, and users as possible. Then evaluate and digest it. Let it challenge your vision but don’t go chasing every idea or feature thrown at you. Resist all impulse to get lost in feature design. Stay focused on who your users are, what they want to do, and what you want them to do.</p>
<h2>#3 Start Hiring. Now.</h2>
<p>When hiring for a small team you want to hire heavy hitters who can help you drive towards the goal line. Unfortunately, so does every other company. Expect it to take at least three months to hire someone (especially the right someone). For engineers, designers, and front-end devs the market is shot. It’s been that way for a while and it’s only going to get worse.</p>
<p>Establish a hiring pipe (personal networks, word of mouth, meetups, even recruiters) and keep it full. Someone who’s not available (or interested) now might be a few months down the road. Don’t wait for positions to really open up; starting filling the pipe as if the position were open now (though I’m not advocating leading people on).</p>
<p>Even for a small team, <a href="http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers">grow them through mentoring</a>. You want them <a href="http://mrdanadams.com/2012/career-story-trajectory-context/">stepping up to new challenges</a> on a regular basis and <a href="http://mrdanadams.com/2012/its-your-company-act-like-an-owner/">taking ownership</a>.</p>
<p>If time is against you, consider hiring a consultant (shameless plug). It’s by no means a silver bullet but the right consultant can add acceleration and experience where it’s sorely needed.</p>
<h2>#4 Understand Your Team</h2>
<p>My first priority with a new project is to get my bearings. To a frustrating extent, nearly everything that comes out of my mouth early on will be a question. I want to know the state of things and how they got that way. This extends far beyond technology.</p>
<ul>
<li>Who are the founders? Who are the key influencers (founders or not)? What is the dynamic between them and the rest of the team? Who makes the decisions? Who really leads?</li>
<li>What technology is in place now? Why was it selected? What issues have they had with it so far?</li>
<li>What are the skills of the current team both technically and non-technically? Do they have good communication skills? Do they understand application architecture? Have they ever scaled anything or worked with large data sets? How do they handle conflict and stress?</li>
</ul>
<p>Keep asking “why” until you really understand your context. If needed, take someone out to lunch or ask small questions in the break room. Tease out perspectives you can’t get in a group setting. Paradoxically, humans are what make software difficult and understanding the team is critical.</p>
<p>While this is especially true for an outsider it’s often true for the core team as well. The better a team understands itself the more it will function as a cohesive unit. A founder who understands his team can utilize their strengths while both protecting against and working on their weaknesses.</p>
<h2>#5 Focus on Quality</h2>
<p>Quality is the visible, tangible thing that gives your users, the press, and investors a perception of value. Make people want to use it and share it. Quality also shows that you’re serious and should be taken seriously.</p>
<p>Make it work. Make it beautiful. Make it fast.</p>
<p>Which factors have been important getting your product off the ground?</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Mentoring & Apprenticeship for Software Engineers]]></title>
<link href="http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers/"/>
<updated>2012-12-07T18:26:00-05:00</updated>
<id>http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers</id>
<content type="html"><![CDATA[<p><img src="http://mrdanadams.com/images/mentoring.jpg" alt="blacksmith glassblower software engineer mentoring apprentice" class="aligncenter" /></p>
<p>What we software engineers (and designers) do is very creative and, in general, best learned through experience. Computer science and color theory can be groked from a book but client communication, handling conflict, and other less tangible disciplines are gained in the trenches. Learning on your own is slow and error-prone; you can miss out on stuff or learn something wrong based on the project work you get or who you happen to work with.</p>
<p>I view the apprenticeship mentoring model for software engineers (as with a blacksmith, glass blower, ceramics or any of the high-skill, difficult to master traditional crafts) to be the best way of accelerating growth, increasing retention, and creating the kind team your organization really needs. What follows is my approach to such an apprenticeship model that works in both client services and product teams.</p>
<!-- more -->
<p>For what it’s worth, I have a good bit of experience with this: I was head of engineering during company growth from 25 to 50 (with a corresponding growth in revenues and projects). This meant going from an engineering team of 6 to an internal team of 15-20 and an off-shore team of about 10. It also required increasing the number of technical project leads from 1 (me) to 5 or 6 through direct mentoring. Those tech leads then themselves became mentors to the new hires as we grew. In my consulting work, I worked with early stage startups, such as <a href="http://www.bytelight.com/" title="Indoor Location">ByteLight</a>, to design and execute hiring as well as plan and stabilize development teams for long-term product development.</p>
<h2>Why Apprenticeship Makes Sense</h2>
<p>Apprenticeship takes effort and dedication and does not come naturally to most individuals or organizations. It requires time and effort that may need to be justified:</p>
<ul>
<li>It accelerates learning on the part of the apprentice in technical and non-technical skills including everything from code quality and language nuances to object design to client communication.</li>
<li>It enables molding junior folks into what you need them to be. With a strong mentor model, you can widen your hiring field since you can bring on candidates that can <a href="http://mrdanadams.com/2012/career-story-trajectory-context" title="software engineer career growth">grow more quickly</a> into something really useful to the business. Associates without mentoring can become a big hiring and project risk and kind of a “boat anchor” that weighs more senior folks down instead of providing valuable support and fresh thinking.</li>
<li>It challenges the mentor to grow as well. Mentoring grows technical, communication, and management skills beyond the limits of normal project work and open up opportunities for getting your people to <a href="http://mrdanadams.com/2012/its-your-company-act-like-an-owner">act like owners</a>.</li>
<li>It encourages dialog and thought around <a href="http://mrdanadams.com/2012/your-career-is-a-business/" title="Software Engineers: You Career is a Business">actively managing their careers</a>. This is good for both the team and management; when growth happens from the bottom up it’s healthier and reduces the overhead load on management.</li>
</ul>
<h2>Success Factors</h2>
<p>There are a few factors that heavily influence the success of mentoring.</p>
<h3>Clearly Defined Roles & Goals</h3>
<p>The mentor and the apprentice should both understand each others roles. It should be something verbally stated to remove any awkwardness or barriers. The mentor is responsible for providing not only support (general advice, answering questions, etc.) but also constructive criticism and recognition for good work. Both of these last two are critical to balance each other out and create a good relationship between mentor and apprentice. The apprentice is responsible to raise their hand when they need help and be pro-active about it. They should be actively looking for areas where they feel weak and pull in the mentor.</p>
<p>Mentoring needs to be clearly defined between the mentor and apprentice. The more ambiguity and awkwardness there is, the more that becomes a barrier to successful mentoring.</p>
<p>Mentoring should be seen as a largely voluntary responsibility. They need to be self-motivated and see themselves in the role.</p>
<h3>Proximity</h3>
<p>Proximity, or lack thereof, is a deal breaker. I prefer that mentor and apprentice be “attached at the hip”. It’s important that they feel very comfortable with each other and have a meaningful context that provides fodder for mentoring. They should talk often, even informally, so that there is a consistent dialog. Ideally the apprentice should sit near the mentor so that when a teaching moment arises the barrier to making that happen is as low as possible. Having to go find the mentor and interrupt them could be a high barrier, especially for junior folks.</p>
<p>They need to have something they both know so they can talk about it. Having to context switch from project work to a side project could be a significant barrier. Project work usually provides the best “fodder” for mentoring. People are already thinking about it and engaged. It naturally provides many “teaching moments” in a way that manufactured mentoring throughout side projects does not.</p>
<p>In either case, if you don’t make time to be working on things in pairs, the mentoring won’t really happen. Mentorship is prohibitively disruptive when it’s a constant interruption to “real” work and involves at least two mental context switches. It requires some dedication to a schedule to make it happen effectively.</p>
<p>This kind of thing can work without being spatially proximate. Pair programming through screenshares and audio chat can be an effective method of working through problems and passing along knowledge. You sometimes lose the non-verbal communications, i.e., being able to read the apprentice’s general level of frustration, but for many folks it can work. With remote team members working from home, knowing how to be effective as a mentor remotely can be challenging but critical. There are a lot of tools at your disposal to make this work; use every one of them.</p>
<p>Whether remote or not, make time for low-intensity touch points. Go grab a coffee or go out to lunch. This helps highlight issues you might be having that otherwise don’t get communicated.</p>
<h3>Support for Mentors</h3>
<p>Mentoring is not a natural thing for most tech leads. It’s a skill that needs to be developed. They should have someone that can provide support. This could be someone with some experience in it or even other mentors.</p>
<p>Acknowledgment that the mentoring investment is worth it needs to start at the top. Management support in the form of explicit goals, advice, time, and resources is critical.</p>
<h3>Every Apprentice is Different</h3>
<p>Apprentice experience level, skill level, interests, and personality and some of the factors requiring that each mentoring situation be customized to the mentor and apprentice in order to be most affective. For instance, with one apprentice they might be really strong code wise and need to focus on client skills and another might be totally different. It’s a balance of what that person needs and what the project context provides opportunity for.</p>
<h2>Doing It</h2>
<h3>The Kickoff</h3>
<p>Any apprenticeship should start with a structured meeting between the mentor, the apprentice, and whoever is heading up the apprenticeship program. The meeting should cover:</p>
<ul>
<li>What mentoring is and why you are doing it (answer: because it makes you better, will make you more successful in your career, will help you enjoy and be more successful in your projects, and helps the business as it grows)</li>
<li>Where they are now in their skills and where they want to go</li>
<li>What everyone wants to get out of it</li>
<li>How it’s going to work: project set up, logistics, communication, going out to lunch together, etc</li>
</ul>
<p>Mentors must know that the hour / day / week they would spend to do the best job of training / pairing with an apprentice is working towards some series of agreed upon goals. Knowing those goals would allow them to make the judgment call whether teaching or doing are the correct goals at any given point.</p>
<h3>On the Same Project</h3>
<p>In this model, mentor and apprentice are on the same project. This is usually a situation where the mentor is the project tech lead and delegates work. They sit near each other so the apprentice can ask questions and get help easily. The mentor frequently checks quality of work and provides feedback (for instance, code review a feature branch before merge).</p>
<p>The mentor should look for opportunities to push the apprentice outside their comfort zone using project work. Give them responsibility on something that is just outside their current abilities and has real consequences. This could be technical such as building a large feature, researching a technology, designing an object model or non-technical such as leading a client meeting, writing a spec, estimating something, or leading some aspect of the project process.</p>
<h4>Challenges</h4>
<p>This requires projects that can support both the mentor and the apprentice in terms of project budget and having work that can be affectively delegated.</p>
<p>One of the biggest challenges is in the staffing plan for projects. Not all projects will be a fit for their respective skill sets. Many teams don’t have a lot of flexibility in mixing and matching project teams for an ideal makeup and staff largely based on current availability. This could be a source of friction to the process and one you’ll need to be diligent about.</p>
<p>It also can change how you sell projects as you need to plan for mentorship hours and some inefficiencies, especially in the work done by the apprentice: all growth requires some degree of failure. Re-examine your task estimates for things that may be delegated to the apprentice.</p>
<p>Be careful to strike a balance between the cost of ramping up apprentice resources and the cost of delivering project features. Budgets may creep to account for “apprentice time” to a point where it effects project budget significantly if left unchecked. Crunch-time pressures also present a challenge to on-project apprenticeship.</p>
<p>One model is to attach the mentor / apprentice and then look for projects for the pair. The opposite approach is also possible in order to lower the resource challenges and budget waste; to find a project fit for the apprentice and then start mentorship with the tech lead on that project. I’m making a big assumption here that apprentices are not put on projects by themselves; having them on a project by themselves can be really risky.</p>
<h3>On Different Projects</h3>
<p>In this model, mentor and apprentice are on different projects but maintain a relationship across projects. They need something they can use as mentor-fodder:</p>
<ul>
<li>The project the apprentice is on. This requires the mentor to stay up on it enough to look for opportunities to mentor with it.</li>
<li>A side project the apprentice is passionate about. The mentor would also have to agree that the side project provided the right opportunity for growth (it’s likely something that encourages technical growth).</li>
</ul>
<p>Using a real project helps the apprentice be more passionate / engaged / successful in their project (which is more valuable to the business than a side project). It also provides scenarios and challenges to the apprentice that a side project doesn’t such as client communication and project process. Working on a side project requires time and energy both of which are usually limited during project work.</p>
<p>These two models could be mixed in order to provide more resourcing options. For instance, you could start in the first model and then, as resource allocation demands, change to the second model to keep up the relationship between mentor / apprentice.</p>
<p>Bootcamp style projects, a non-billable project geared towards getting new recruits ramped up on our practices, can work in the right context but require a fair bit of management, mentorship, and coordination outside of billable work. The “shadow” apprenticeship model on real billable work may be more practical for most organizations.</p>
<h3>Resource Allocation & Project Pressures</h3>
<p>Each project has it’s particulars and, just as you consider the situation for who would be the best fit as a team, consider whether it’s a fit for mentoring; the risk level and what you could get out of it. It could be very tough to mentor on a “heads down” project but there may be teaching opportunities there as well. In fact, a heads down project may be just the kind of catalyst for growth in a more senior person where it wouldn’t be a fit for apprenticeship of younger staff. The decision to go heads down and crank (and how to do and handle that) is, in itself, something a junior person could learn. Learning to manage project pace and intensity is a valuable skill.</p>
<p>Mentorship is a distraction. It is a context switch and that’s okay. Teaching and doing are very different goals and, at any given moment, one goal or the other should be the highest priority. If it’s crunch time, and delivering the product is the highest priority, you might put teaching on the back burner and focus on doing good work quickly. But at other times you would prioritize training and acknowledge the slower production time in favor of allowing the apprentice to work at his own pace.</p>
<p>The ratio of mentoring to project time could be estimated (and should be capped) but likely won’t be consistent. Mentoring with the context of a project (such as code reviews) can be a really effective method of upping quality, lowering risk, and lowing cost so it doesn’t always need to be seen as waste or overhead. For instance, code reviews are an excellent way of teaching while catching bugs before they make it into production code.</p>
<p>Mentoring is much less of a distraction if it’s in the context of normal project work. If two people are jogging together, it’s much more natural for one person to teach the other person better jogging form while they keep running. It would be highly disruptive if you were jogging and suddenly one of you said “now stop so I can show you how to do good pushups”.</p>
<p>Expect mentoring to need case-by-case consideration with resourcing to ensure that it’s done affectively without significantly negatively effecting project budget, quality, or overall success. Mentoring (and having junior folks in general) does take investment on the part of the company but that investment should be as known and controlled as possible.</p>
<h2>The End Game: Make the Mentor Redundant</h2>
<p>A mentor should, in most cases, be growing the apprentice in whatever way is necessary that the apprentice could eventually do their job. In many cases, this actually happens: the mentor starts as the project lead and by the end of the project the apprentice has taken the reins. A good example of this is a new project that has a heavy, green field development phase followed by a slower maintenance phase. The first provides an opportunity for the apprentice to lead the process, the product, and the technical machinery and possibly take the lead for the maintenance phase. This will be a challenge for them but is much simpler than green field development.</p>
<p>This isn’t always the case; you could also have an end game of an apprentice simply getting better at something specific. But, in general, you should be growing them to be well-rounded contributors. This typically is better for them and for the business.</p>
<h2>Growth is Not Just for Apprentices</h2>
<p>A well-rounded view of growth affords everyone opportunities for growth and improvement. For more senior folks, this means they don’t need apprenticeship but rather support in other ways from their peers and management. In many ways, growing senior folks is more difficult than apprenticeship since, as people grow, the challenges they need to encounter in order to keep them growing become very specific to that person.</p>
<h2>Your Mileage May Vary</h2>
<p>The form that mentorship takes (or should take) can vary significantly by the team and individual. What has and hasn’t worked for you? If you were without mentoring, is it something you could have benefited from?</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Trello for Agile Development at BlockAvenue]]></title>
<link href="http://mrdanadams.com/2012/trello-agile/"/>
<updated>2012-12-05T16:40:00-05:00</updated>
<id>http://mrdanadams.com/2012/trello-agile</id>
<content type="html"><![CDATA[<p>One of the my roles as CTO at <a href="http://www.blockavenue.com/" title="Neighborhood Reviews">BlockAvenue</a> is to manage a <a href="http://blog.blockavenue.com/corporate/blockavenue-your-neighborhood-socialized/">high velocity, agile development process</a>. <a href="https://trello.com/">Trello</a> is my new favorite tool when it comes to ticket management but it’s not without limitations. Here’s how we use Trello at BlockAvenue and some of the challenges working with it.</p>
<!-- more -->
<h2>The Basics</h2>
<p>Cards are arranged into buckets (Trello lists) each bucket representing how far in the future it’s cards will get done. The further out the bucket, the larger and less well-groomed it may be.</p>
<p>Lists are arranged left-to-right so that the most frequently used lists are on the first screen for most displays, the less used lists being to the right on the second screen. They also follow a logical progression of <em>queued -> doing -> done</em>.</p>
<p><a href="http://mrdanadams.com/images/trello/board.png"><img src='http://mrdanadams.com/images/trello/board_600x400.png' /></a></p>
<ul>
<li><strong>Dec 2012:</strong> All of the priority cards for this month based on business priorities and other factors. This will map to multiple staging or production releases (even multiple releases each week) but all of the development in this month will be pulled from this bucket. Cards in this list are sorted roughly in order of priority. Most are assigned owners.</li>
<li><strong>Jan 2013:</strong> Similar to the Dec list, this is for the following month but less well defined.</li>
<li><strong>General Backlog:</strong> This list represents a larger backlog to be pulled from into the monthly lists. It contains all things that should get done at some point but didn’t make the cut. It can be thought of as the mid-term roadmap.</li>
<li><strong>Future:</strong> A holding area for future ideas. When you have an idea, don’t be afraid to make a card for it. File it away so it doesn’t get lost. This is list is groomed the least often.</li>
</ul>
<p>In addition to the time frame-based lists, there are a few special ones:</p>
<ul>
<li><strong>Inbox:</strong> All new cards go here. Cards are held until they can get triaged and moved somewhere else. This keeps the dev team sane and ensures the non-devs know their cards aren’t getting lost. It should be empty most of the time.</li>
<li><strong>On Deck:</strong> Cards pulled from the current monthly milestone that are next up for development. You can think of this roughly as a weekly iteration list. This list should be fairly short and reviewed by the team every couple days (or even daily).</li>
<li><strong>Doing:</strong> Cards pulled from On Deck that are being worked on.</li>
<li><strong>Done:</strong> Completed cards. Note that we don’t have lists indicating the status of a done card (in testing, deployed, etc). This is only because we haven’t needed it but for some teams managing the work flow around Done cards is probably a good idea. For instance, you may need explicit design QA on a feature and there could be a list for those cards.</li>
</ul>
<p>The Done list gets some special treatment: whenever a production release is made we rename the “Done” to something like “Dec 3, 2012 Release”, archive it, and create a new “Done” list. This captures for posterity what was released and allows anyone to know what’s done so far in the current release just by looking at the Done list. You may also want to tag the card with the git release tag.</p>
<h2>Anatomy of a Good Card</h2>
<p>Well-formed cards encourage getting the work done smoothly and accurately. While you may not need all of the following on each card, I find myself using using all of these to some degree overall.</p>
<h3>Card Title, Tags, and Labels</h3>
<p><img src="http://mrdanadams.com/images/trello/card-top.png" alt="A good Trello card title" /></p>
<ul>
<li>Keep the title succinct. Put the long stuff in the card description. A long title will severly reduce how many cards you can see at once in your lists.</li>
<li>Tags for things like which users it affects, browser, platform, if it’s an epic or story, etc</li>
<li>Use labels to visually indicate traits of the card and allow card filtering</li>
<li>Work estimate. A good convention is “(5) Do something” especially when it’s supported by <a href="https://chrome.google.com/webstore/detail/trello-scrum/jdbcdblgjdpmfninkoogcfpnkjmndgje">Trello Scrum</a>.</li>
</ul>
<p><img src="http://mrdanadams.com/images/trello/story-title.png" alt="Trello card with estimate" /></p>
<h3>Card Description</h3>
<ul>
<li>Be sure to write one unless the title, tags, and labels provide everything needed to get it done</li>
<li>Take full advantage of markdown. Include code snippets, samples of log files, bulleted lists, headers, etc.</li>
<li>Write a full spec for complex cards. Provide implementation hints / details.</li>
<li>Include reasons <em>why</em> the card is being done based on team discussion. This info tends to get lost and can be really valuable later.</li>
</ul>
<h3>Screenshots & Attachments</h3>
<p><img src="http://mrdanadams.com/images/trello/attachments.png" alt="Trello attachments" /></p>
<p>Don’t try to describe a bug with words if it’s visual. Just don’t. Take a screenshot (or multiple screenshots) with something like <a href="http://evernote.com/skitch/">Skitch</a> and attach it. Especially during design QA, designers can get across exactly what they’re are talking about.</p>
<p><img src="http://mrdanadams.com/images/trello/card.png" alt="Trello card with image" class="alignright" /> For new features, attach images of the designs for reference even if the PSDs live in Dropbox. A side effect is the visual appeal it can add to your lists.</p>
<h3>Checklist(s)</h3>
<p><img src="http://mrdanadams.com/images/trello/checklists.png" alt="Trello card checklists" /></p>
<p>Use checklists to guide work and track detail progress.</p>
<h2>Trello and git</h2>
<p>We have two primary git branches: <em>master</em> (which always represents what’s on production) and <em>development</em> which is the current release under development and is frequently pushed to staging. Trivial and small cards can be done right on development but larger cards (or those involving multiple commits) get moved to their own feature branch. While not displayed prominently, Trello cards actually do have unique, auto-incrementing numbers:</p>
<p><img src="http://mrdanadams.com/images/trello/trello_card_number.png" alt="Trello card numbers" /></p>
<p>All feature branches map back to a Trello card based on the card number (ex: <em>322_my_awesome_new_feature</em>). You could also include the initials of the person that created the branch in a larger team.</p>
<h2>Labels, Tags, and Finding Your Cards</h2>
<p><img src="http://mrdanadams.com/images/trello/trello_labels.png" alt="BlockAvenue Trello labels" class="alignright" /></p>
<p>Trello lets you easily filter by person or label. You only get six labels so use them wisely; it’s a pain to go back and change later. Here’s the labels we used:</p>
<ul>
<li><strong>Data</strong>, <strong>App</strong>, <strong>API</strong>, <strong>Admin</strong>: Major system components</li>
<li><strong>Bug</strong>: Something that’s broken.</li>
<li><strong>Needs Review</strong>: Needs clarification or discussion and isn’t ready to be worked on. When cards are pulled into <em>On Deck</em> none should have this label.</li>
</ul>
<p>Keep in mind that one of the best thing about labels is that they are color coded making it good for visually distinguishing cards when the lists are unfiltered.</p>
<p>You will inevitably come up with ways you want to filter your cards that don’t fit owners, due dates, or labels. While Trello doesn’t have direct support for tags it does have a nice text search. It only searches the title and ignores any non-alphanumeric characters. With <em>#sometag</em> on the end of a card title you can see that a card is tagged and, as long as you pick a tag name that isn’t common to your card titles, you can filter by it.</p>
<p><img src="http://mrdanadams.com/images/trello/tags.png" alt="Trello tags" /></p>
<p>Suggestions for tags:</p>
<ul>
<li><strong>Browser-specific:</strong> #ie9, #ff, #safari</li>
<li><strong>User group:</strong> #dweller, #mover, #press</li>
<li><strong>Card types:</strong> #epic, #story</li>
</ul>
<h2>Checklists Rock</h2>
<p>Lists are awesome. Sometimes it seems my whole life is managed by making lists of things. Trello is no different. Tips on using checklists:</p>
<ul>
<li>When grooming the backlog, create the checklist for a card to get a sense of what’s involved.</li>
<li>Use multiple checklists with custom titles to separate between components of a card (frontend, backend) or between roles (dev, design).</li>
<li>You can’t (yet) drag TODOs betweens checklists. Bummer.</li>
</ul>
<h2>Dealing with Long Lists</h2>
<p>Lists can get long, especially if you attach images or use long card titles. Unfortunately, there isn’t a ‘send to top’ for cards but if you drop a card on a list quickly it will shoot to the bottom of the list. Try to keep your titles succinct or even reducing the browser font size as small as you can stand it. It’s a hack, but it works.</p>
<h2>Card Estimates</h2>
<p>This is, perhaps, the single biggest thing I miss in Trello; the ability to put a work estimate on a card and see estimate totals for each list. Even with a small backlog, tracking estimates can add much needed control to the process. <a href="http://bluelinegamestudios.com/trello/">this tool</a> allows you to track estimates and graph them but it’s not a great solution: the estimates should be managed in Trello.</p>
<p><a href="https://chrome.google.com/webstore/detail/trello-scrum/jdbcdblgjdpmfninkoogcfpnkjmndgje">Trello Scrum</a> looks promising although I don’t believe it does any kind of burn visualization.</p>
<p><img src="https://lh4.googleusercontent.com/U9TS8KIksA5LyG0u2AGSmB6HNBQuRS1Xx2Zsf5j74AW2FHCT4WKhnXG372FGqi0O7NE7qrgIew=s640-h400-e365" alt="Trello Scrum" /></p>
<h2>Shortcuts</h2>
<p>If you aren’t using <a href="https://trello.com/shortcuts">Trello shortcuts</a>, you should be. They will save you a massive amount of time when grooming the backlog.</p>
<h2>Team Friction</h2>
<p>We struggled with effectively using Trello to make the business stakeholders self-sufficient in answering the following:</p>
<ul>
<li>How much is left and when will it get done?</li>
<li>Given our current team, how much can we get done in the next N weeks?</li>
</ul>
<p>The following goes a long way to solving this:</p>
<ul>
<li>Good team communication and regular review of the lists</li>
<li>Good card and list hygiene: well-formed cards and groomed lists</li>
<li>Date or goal-oriented lists</li>
<li>Card estimates and tracking team velocity</li>
</ul>
<h2>For Hackers: Card JSON and Trello API</h2>
<p>Trello’s beauty is in it’s simplicity. When it doesn’t allow you to do something you always have the option of using the <a href="https://trello.com/docs/api/index.html">Trello API</a>. Tucked away under the card details is a link to the card JSON:</p>
<p><img src="http://mrdanadams.com/images/trello/card-json.png" alt="Trello card JSON" /></p>
<h2>When I Wouldn’t Use Trello</h2>
<p>Trello is simple and necessarily limited. In a more complex project I could see easily outgrowing it. <a href="http://www.atlassian.com/software/jira/overview/">OnDemand JIRA</a> with <a href="http://www.atlassian.com/software/greenhopper/overview">GreenHopper</a> is a great alternative. Unlike Trello, it’s not free but it’s the Swiss Army knife of ticket tracking as far as I’m concerned. Between Trello and JIRA I have a hard time recommending anything else (having used most of the others).</p>
<p>An example where Trello might not fit is a software platform that needed to track things across versions of the software. A REST API with multiple versions could need maintenance fixes and releases against old versions. In JIRA, this is relatively trivial with version tracking. That’s not to say you <em>couldn’t</em> do it with Trello but it could get messy very quickly.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[5-10x Speed Ups by Pipeling Multiple REDIS Commands in Ruby]]></title>
<link href="http://mrdanadams.com/2012/pipeline-redis-commands-ruby/"/>
<updated>2012-11-08T14:36:00-05:00</updated>
<id>http://mrdanadams.com/2012/pipeline-redis-commands-ruby</id>
<content type="html"><![CDATA[<p><strong>NOTE:</strong> This is now on github at <a href="https://github.com/mrdanadams/redis_pipeliner">https://github.com/mrdanadams/redis_pipeliner</a>.</p>
<p><a href="http://redis.io">REDIS</a> is fast. Really fast. And awesome. But that doesn’t mean you can get lazy with how you use it. <a href="http://redis.io/topics/pipelining">Pipelining REDIS commands</a> allows you to execute multiple commands in REDIS and return the results at once. I’ve seen this provide a 5x-10x speedup. He’s how to do it with the Ruby driver.</p>
<!-- more -->
<h2>Why You Should Pipeline</h2>
<p>(If your code isn’t performance sensitive the following is probably unnecessary.) When working with REDIS, look out for code blocks that look like this:</p>
<ol>
<li>ask REDIS for something</li>
<li>do something with it</li>
<li>repeat</li>
</ol>
<p>Depending on the number of calls you make, you could be paying a lot in over-the-wire and connection overhead (even within the same data center). A better model is to do something like this:</p>
<ol>
<li>collect all your commands</li>
<li>send them to REDIS</li>
<li>unpack the result and do something with it</li>
</ol>
<p>Even with REDIS being very, very fast and the server being close to the client, this can lead to significant improvements in speed. I’ve seen a handful of commands (< 10) pipelined to drop a 250ms page render to 50ms.</p>
<h2>Get Your Pipe On</h2>
<p>Here’s the general structure of using the Ruby driver to pipeline multiple REDIS commands. When you execute a command, rather than getting the result you get a <a href="https://github.com/redis/redis-rb#futures">Future</a> object which you can ask for the result when it’s available.</p>
<p>Using Future comes with a few challenges:</p>
<ul>
<li>If you are executing the command based on some context (such as a related object) you need that context when you get the result</li>
<li>Future doesn’t provide a nice way of waiting for the value to be realized or even checking if it has been</li>
</ul>
<p>In this example, we’ll iterate through a number of objects each one needing a REDIS hash.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1"># we'll collect a number of Futures with their related context</span>
</span><span class='line'><span class="c1"># Let's pretend have a number of objects and need to get their associated data</span>
</span><span class='line'><span class="n">cmds</span> <span class="o">=</span> <span class="o">[]</span>
</span><span class='line'><span class="n">objects</span> <span class="o">=</span> <span class="o">[]</span> <span class="c1"># we'll pretend this isn't empty</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># Create the pipelined commands. Any REDIS commands executing in this block will return Futures rather than values</span>
</span><span class='line'><span class="n">redis</span><span class="o">.</span><span class="n">pipelined</span> <span class="k">do</span>
</span><span class='line'> <span class="n">objects</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">object</span><span class="o">|</span>
</span><span class='line'> <span class="n">cmds</span> <span class="o"><<</span> <span class="p">{</span> <span class="ss">future</span><span class="p">:</span> <span class="n">redis</span><span class="o">.</span><span class="n">hgetall</span><span class="p">(</span><span class="n">object</span><span class="o">.</span><span class="n">key</span><span class="p">),</span> <span class="ss">object</span><span class="p">:</span> <span class="n">object</span> <span class="p">}</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="c1"># Now that we have our commands executing we need to wait for the values to come back</span>
</span><span class='line'><span class="n">cmds</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>
</span><span class='line'> <span class="c1"># wait for each one to come back before proceeding. this is not ideal but simple and seems to work fine.</span>
</span><span class='line'> <span class="k">while</span> <span class="n">c</span><span class="o">[</span><span class="ss">:future</span><span class="o">].</span><span class="n">value</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="ss">Redis</span><span class="p">:</span><span class="ss">:FutureNotReady</span><span class="p">)</span>
</span><span class='line'> <span class="nb">sleep</span><span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span> <span class="o">/</span> <span class="mi">100</span><span class="o">.</span><span class="mi">0</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="n">value</span> <span class="o">=</span> <span class="n">c</span><span class="o">[</span><span class="ss">:future</span><span class="o">].</span><span class="n">value</span>
</span><span class='line'> <span class="n">object</span> <span class="o">=</span> <span class="n">c</span><span class="o">[</span><span class="ss">:object</span><span class="o">]</span>
</span><span class='line'>
</span><span class='line'> <span class="c1"># do something with the REDIS return value and our context</span>
</span><span class='line'> <span class="n">object</span><span class="o">.</span><span class="n">values</span> <span class="o">=</span> <span class="n">value</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Improvements</h2>
<p>You can see that the model here is pretty disjointed and makes for less-maintainable code. The problem is having to break your code up causing you to lose context. Something like this might make more sense (<strong>warning</strong> untested ruby pseudocode ahead):</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">Pipeliner</span><span class="o">.</span><span class="n">pipeline</span> <span class="n">redis</span> <span class="k">do</span> <span class="o">|</span><span class="n">pipe</span><span class="o">|</span>
</span><span class='line'> <span class="n">objects</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">object</span><span class="o">|</span>
</span><span class='line'> <span class="n">pipe</span><span class="o">.</span><span class="n">enqueue</span> <span class="n">redis</span><span class="o">.</span><span class="n">hgetall</span><span class="p">(</span><span class="n">object</span><span class="o">.</span><span class="n">key</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">result</span><span class="o">|</span>
</span><span class='line'> <span class="n">object</span><span class="o">.</span><span class="n">values</span> <span class="o">=</span> <span class="n">result</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>Pipeline</code> could look like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">class</span> <span class="nc">Pipeliner</span>
</span><span class='line'> <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">redis</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@redis</span> <span class="o">=</span> <span class="n">redis</span>
</span><span class='line'> <span class="vi">@cmds</span> <span class="o">=</span> <span class="o">[]</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">enqueue</span><span class="p">(</span><span class="n">future</span><span class="p">,</span> <span class="o">&</span><span class="nb">proc</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@cmds</span> <span class="o"><<</span> <span class="p">{</span> <span class="ss">future</span><span class="p">:</span> <span class="n">future</span><span class="p">,</span> <span class="ss">callback</span><span class="p">:</span> <span class="nb">proc</span> <span class="p">}</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">wait</span>
</span><span class='line'> <span class="vi">@cmds</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>
</span><span class='line'> <span class="k">while</span> <span class="n">c</span><span class="o">[</span><span class="ss">:future</span><span class="o">].</span><span class="n">value</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="ss">Redis</span><span class="p">:</span><span class="ss">:FutureNotReady</span><span class="p">)</span>
</span><span class='line'> <span class="nb">sleep</span><span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span> <span class="o">/</span> <span class="mi">100</span><span class="o">.</span><span class="mi">0</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="n">c</span><span class="o">[</span><span class="ss">:callback</span><span class="o">].</span><span class="n">call</span> <span class="n">c</span><span class="o">[</span><span class="ss">:future</span><span class="o">].</span><span class="n">value</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">pipeline</span><span class="p">(</span><span class="n">redis</span><span class="p">,</span> <span class="o">&</span><span class="nb">proc</span><span class="p">)</span>
</span><span class='line'> <span class="c1"># Executes callbacks with each result. This blocks.</span>
</span><span class='line'> <span class="n">pipeliner</span> <span class="o">=</span> <span class="no">Pipeliner</span><span class="o">.</span><span class="n">new</span> <span class="n">redis</span>
</span><span class='line'> <span class="n">redis</span><span class="o">.</span><span class="n">pipelined</span> <span class="k">do</span>
</span><span class='line'> <span class="nb">proc</span><span class="o">.</span><span class="n">call</span> <span class="n">pipeliner</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="n">pipeliner</span><span class="o">.</span><span class="n">wait</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Embedding Backbone Templates in Rails with CoffeeScript]]></title>
<link href="http://mrdanadams.com/2012/backbone-templates-rails-coffeescript/"/>
<updated>2012-10-12T12:30:00-04:00</updated>
<id>http://mrdanadams.com/2012/backbone-templates-rails-coffeescript</id>
<content type="html"><![CDATA[<p><a href="http://coffeescript.org/#strings">Multiline string support in CoffeeScript</a> offers a great way to embed your <a href="http://backbonejs.org/#View-render">Backbone templates</a>. In this approach, you can easily edit your templates while still taking advantage of JS minification, combination, and client-side caching.</p>
<!-- more -->
<h2>Registering and caching templates</h2>
<p>We’ll create a top-level object in our namespace to hold our templates which are cached for efficient use across many view instances.</p>
<div><script src='https://gist.github.com/3880219.js?file=templates.coffee'></script>
<noscript><pre><code># Our top-level namespace (which I'll just call NS for sample purposes)
NS = @NS
NS.Templates =
templates: {} # empty object to cache compiled templates
# gets a compiled template rendering by ID
get: (id) -> @templates[id]
# caches a new template
register: (id, source) -> @templates[id] = _.template(source)
# Register a template embedding the sources in a multi-line string
NS.Templates.register "ourAwesomeTemplate", """
<div class="inner">
<h3><%= name %></h3>
</div>
"""
NS.Templates.register "anotherAwesomeTemplate", """
<span><%= foo %></span>
"""
</code></pre></noscript></div>
<h2>Using the templates</h2>
<p>An example of using the template in a Backbone view:</p>
<div><script src='https://gist.github.com/3880219.js?file=view.coffee'></script>
<noscript><pre><code>NS = @NS
NS.View.SomethingGreat = @Backbone.View.extend
className:'my-awesome-thing'
initialize: (options) ->
# could also do this in render()
@template = NS.Templates.get('ourAwesomeTemplate')
this
render: ->
$(@el).html(@template(@model.toJSON()))
this
</code></pre></noscript></div>
<p>In this case I’m registering and caching our templates on load. If we had more templates, or they were used less often, we could register them to be compiled on-demand which might be more efficient.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MongoHQ vs MongoLab: Selecting a Hosted MongoDB Provider]]></title>
<link href="http://mrdanadams.com/2012/mongohq-mongolab-mongodb-customer-service/"/>
<updated>2012-09-26T10:19:00-04:00</updated>
<id>http://mrdanadams.com/2012/mongohq-mongolab-mongodb-customer-service</id>
<content type="html"><![CDATA[<p><img class="feature" src="http://mrdanadams.com/images/mongohq_mongolab.jpg" title="MongoLab vs MongoHQ" ></p>
<p>In the course of building <a href="http://blockavenue.com">BlockAvenue</a>, one of the decisions I made was whether to host our own <a href="http://www.mongodb.org">MongoDB</a> or use a hosted provider. Here’s the short tale of what I selected and the importance of top-tier customer service.</p>
<!-- more -->
<h2>Self-hosted or managed hosting?</h2>
<p>We were under intense deadline, had no resources to spend setting up or maintaining a MongoDB cluster, needed no heavy customizations, and needed the database up and running yesterday. Since the app was Heroku hosted, we also needed some control over which region it would be located in. The decision to use a hosted service was a no-brainer even if it meant higher costs for the EC2 instances in the short term. <a href="https://www.mongohq.com">MongoHQ</a> and <a href="https://mongolab.com">MongoLab</a> were clearly the top two choices. But which provider to use? And which size deployment?</p>
<h2>A first-look at MongoHQ and MongoLab</h2>
<p>My first impression comparing MongoHQ and MongoLab put me slightly in favor of MongoLab. In fact, we used the free plan on MongoLab for a short period (and I’ve used them for small stuff on other projects). As a customer walking in the door, I found the MongoLab pricing and info about their offering much clearer and more informative. Even when choosing a database, having clear pricing and product info is important and eye candy makes some difference. The MongoHQ page provides the basics on pricing but not much else.</p>
<p>With their dedicated plans, for instance, you know the pricing for a Large instance without a replica set ($525 / mo) and the Extra Large instance with replica set ($1,950 / mo). But what about starting with a single Extra Large instance and going to replica set later? How do I expand my cluster? What’s the process and costs involved?</p>
<p><img src="http://mrdanadams.com/images/mongohq_pricing.png" title="MongoHQ pricing" ></p>
<p>The MongoLab documentation is much clearer and their technical architecture page (aka “What will I get?”) makes a <strong>lot</strong> of sense is and very well done. Overall, MongoLab was more informative and, frankly, better looking. Even when choosing a back end technology, visual goodness conveys a sense of quality.</p>
<p><img src="http://mrdanadams.com/images/mongolab_pricing.png" title="MongoLab pricing" ></p>
<p><img src="http://mrdanadams.com/images/mongolab_arch.jpg" title="MongoLab architecture" ></p>
<h2>Interaction with a real human</h2>
<p>At this point, I’m on the fence and out of time; I need a deployment up, my data loaded, and to start setting up the production environment. I still have big questions about the specifics of the data we are handling; large amounts of geospatial data with very fast response times and a lot of queries.</p>
<p>I email <a href="http://www.10gen.com">10gen</a> asking to get on the phone with someone to ask a few questions about index structure, the amount of memory needed and overhead to plan for, and other performance concerns. Within 30 minutes I am on the phone with a 10gen engineer. Awesome. I could not have had a better experience with 10gen and would absolutely recommend people with non-trivial deployments consider a support contract or initial consultation.</p>
<p>Having my strategy worked out, I need to select a vendor and get the deployment up. I fire off emails to MongoHQ and MongoLab. Within 10 minutes, a real person with real experience and knowledge of MongoDB calls my cell (Chris from Birmingham). Chris is awesome; I explain our project particulars and he immediately comes back with solid questions and recommendations based on their experiences with other customers. 15 minutes on the phone and I am ready to pull the trigger. This was, without a doubt, one of the best customer experiences I’ve ever had (I sent him an email later saying as much).</p>
<p>I walk back to my desk, create my account, set up a new deployment, wait for the EC2 instances to come up (which took a few minutes), create my database, and am off and running. 30 minutes in and I have my instance with GBs of data and many millions of documents loaded and serving our Heroku-hosted app. Everything should be this easy. Rock on, MongoHQ.</p>
<p>I hear back from MongoLab several hours later (which, admittedly, isn’t <strong>that</strong> bad) but MongoHQ beat them to it and totally delighted me with their customer service. My production db was up and serving our app by the time MongoLab responded.</p>
<h2>Living with MongoHQ</h2>
<p>A few days later, we need to make the move to a replica set in preparation for <a href="http://blog.blockavenue.com/corporate/we-are-live-n-kickin/">go-live</a>. I email support with specific questions about cost, process, time line, and scaling up / down as needed based on traffic (<a href="http://techcrunch.com/2012/09/20/blockavenue-launch">we knew we were going to get TechCrunch’d</a>). Again, I have a response within minutes. A day or two later, on a Saturday, we circle back on this and are ready to make a decision. Email is sent to support giving them the go-ahead to both increase the instance size and create the replica set. Response was that they are on it but may not get to it by Monday. A few hours later, I’m delighted to see an email that the new cluster is up with the URLs for each server. Again, MongoHQ goes above expectations for customer support.</p>
<p>Once up and running, the clusters are largely self-service and we’ve had no problems so far. An admin panel shows the status of each node, connection and database info, and what’s happening on each instance now and in recent history. The old admin is a bit ugly and dated but the new admin (in alpha) is already awesome. It’s also responsive meaning it works well as a large view showing and as a mini dashboard. In fact, on launch day, I left it running in the corner of my second monitor all day to keep an eye on things.</p>
<h2>Final thoughts</h2>
<p>In the end, the offerings between MongoHQ and MongoLab may be marginally different, at least in terms of pricing and technology. However, MongoHQ support has so far proven to be top notch. For many of my clients without IT or dev ops resources, this makes the price increase over running their own MongoDB cluster on EC2 well worth it.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Creating a custom OpenLayers build by profiling usage]]></title>
<link href="http://mrdanadams.com/2012/custom-openlayers-build/"/>
<updated>2012-08-18T12:45:00-04:00</updated>
<id>http://mrdanadams.com/2012/custom-openlayers-build</id>
<content type="html"><![CDATA[<p><a href="http://openlayers.org/">OpenLayers</a> is a great library for integrating maps especially when creating custom mapping interfaces. However, with great functionality can come great size. Weighing in at nearly 1MB for the full library, you should pair it down to only the modules used.</p>
<p><a href="https://github.com/mrdanadams/openlayers-instrumenter">openlayers-instrumenter</a> creates a custom OpenLayers build profile by instrumenting the JavaScript dynamically and tracking what’s used.</p>
<!-- more -->
<h2>Instrument the OpenLayers JS</h2>
<p>Load the instrumenter after OpenLayers but before using it:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><script </span><span class="na">src=</span><span class="s">"lib/OpenLayers-2.12/lib/OpenLayers.js"</span><span class="nt">></script></span>
</span><span class='line'><span class="nt"><script </span><span class="na">src=</span><span class="s">"lib/openlayers-instrumenter.js"</span><span class="nt">></script></span>
</span><span class='line'><span class="nt"><script </span><span class="na">src=</span><span class="s">"you-app-code.js"</span><span class="nt">></script></span>
</span></code></pre></td></tr></table></div></figure>
<p> <strong>Note:</strong> It’s not for production use so only include it while creating the build profile.</p>
<p>Load your page. About 2 seconds after page load, you’ll see something like this console.log’d:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'>[first]
</span><span class='line'>
</span><span class='line'>[last]
</span><span class='line'>
</span><span class='line'>[include]
</span><span class='line'>OpenLayers/Map.js
</span><span class='line'>OpenLayers/BaseTypes/Size.js
</span><span class='line'>OpenLayers/BaseTypes/Bounds.js
</span><span class='line'>OpenLayers/Util.js
</span><span class='line'>OpenLayers/BaseTypes/Element.js
</span><span class='line'>OpenLayers/Events.js
</span><span class='line'>OpenLayers/Function.js
</span><span class='line'>OpenLayers/Event.js
</span><span class='line'>OpenLayers/String.js
</span><span class='line'>OpenLayers/Control/Navigation.js
</span><span class='line'>OpenLayers/Control/Zoom.js
</span><span class='line'>OpenLayers/Control/ArgParser.js
</span><span class='line'>OpenLayers/Control/Attribution.js
</span><span class='line'>OpenLayers/Handler/Click.js
</span><span class='line'>OpenLayers/Control/DragPan.js
</span><span class='line'>OpenLayers/Control/ZoomBox.js
</span><span class='line'>OpenLayers/Handler/Drag.js
</span><span class='line'>OpenLayers/Handler/Box.js
</span><span class='line'>OpenLayers/Handler/MouseWheel.js
</span><span class='line'>OpenLayers/Control/PinchZoom.js
</span><span class='line'>OpenLayers/Handler/Pinch.js
</span><span class='line'>OpenLayers/Events/buttonclick.js
</span><span class='line'>OpenLayers/Layer/WMS.js
</span><span class='line'>OpenLayers/Layer/Vector.js
</span><span class='line'>OpenLayers/Renderer/SVG.js
</span><span class='line'>OpenLayers/StyleMap.js
</span><span class='line'>OpenLayers/Style.js
</span><span class='line'>OpenLayers/Projection.js
</span><span class='line'>OpenLayers/Control/LayerSwitcher.js
</span><span class='line'>OpenLayers/Lang.js