├── .gitignore ├── assets └── hacker-guide-to-setting-up-your-mac │ └── terminal.png ├── the-birth-of-bluu.md ├── readme.md ├── an-indian-beginning.md ├── bluu-testflight-crashlytics-hockeyapp.md ├── bluu-app-store-submission.md ├── bluu-the-rules.md ├── say-hello-to-gittask.md ├── principles-of-an-ideal-database-client.md └── hacker-guide-to-setting-up-your-mac.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /assets/hacker-guide-to-setting-up-your-mac/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lapwinglabs/blog/HEAD/assets/hacker-guide-to-setting-up-your-mac/terminal.png -------------------------------------------------------------------------------- /the-birth-of-bluu.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: The Birth of Bluu 3 | featured: https://i.cloudup.com/kvPq_1dMQm.jpg 4 | tags: bluu, app, design, game, iOS, Android 5 | date: August 1, 2014 6 | slug: the-birth-of-bluu 7 | excerpt: We wanted to make a app that could be usable by the end of a weekend. 8 | author: Andy Pai 9 | ``` 10 | 11 | We wanted to make an app that could be usable by the end of the weekend. The hope was to get an app published to the Apple app store quickly to start learning the intricacies of beta deployment and testing, app store submission, and post-release product marketing. 12 | 13 | After discussing ideas ranging from utility to fantasy sports apps, we decided to work on a simple game. We figured since games get downloaded 5x-10x more than any other category, it gave us the greatest chance of getting some adoption. After doing some research on other popular games like 2048 and Timberman, we wanted the game to have the following characteristics: 14 | 15 | 1. Easy to play 16 | 2. Hard to master 17 | 3. Simple graphics 18 | 19 | We thought the flicking and combing of tiles in 2048 made for a pretty awesome experience so we wondered if matching colors instead was numbers could provide similar satisfaction. Having taken a bit of psychology in high-school and college, we remembered that it's harder to read words if they're colored differently than that what they mean. 20 | 21 | The first mock-ups: 22 | 23 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Initial-Thoughts.JPG) 24 | 25 | The second mock-up: 26 | 27 |
28 |
29 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Mock-up1.png) 30 |
31 |
32 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Mock-up2.png) 33 |
34 |
35 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Mock-up3.png) 36 |
37 |
38 |
39 | 40 | Final Design: 41 | 42 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Final1.png) 43 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Final2.png) 44 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Final3.png) 45 | 46 | 47 | In [Bluu: The Rules](http://lapwinglabs.com/blog/bluu-the-rules) we describe our next steps in designing the rule logic. 48 | 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | 3 | [Principles of an Ideal Database Client](principles-of-an-ideal-database-client.md) 4 | 5 | ``` 6 | title: Principles of an Ideal Database Client 7 | tags: [database, client, open source, development] 8 | date: January 25, 2015 9 | slug: principles-of-an-ideal-database-client 10 | excerpt: Discussing the principles that go into an ideal database client 11 | author: Matthew Mueller 12 | ``` 13 | 14 | [Say Hello to Gittask](say-hello-to-gittask.md) 15 | 16 | ``` 17 | title: Say Hello to Gittask 18 | tags: [gittask, site, open source, development] 19 | date: January 16, 2015 20 | slug: say-hello-to-gittask 21 | excerpt: Introducing a new marketplace for coding tasks 22 | author: Lapwing Labs 23 | draft: true 24 | ``` 25 | 26 | [Hacker's Guide to Setting up your Mac](hacker-guide-to-setting-up-your-mac.md) 27 | 28 | ``` 29 | title: Hacker's Guide to Setting up your Mac 30 | tags: [mac, hacker, bash] 31 | date: September 30, 2014 32 | slug: hacker-guide-to-setting-up-your-mac 33 | excerpt: Setting up your Mac the Hacker way 34 | author: Matthew Mueller 35 | ``` 36 | 37 | [Bluu: App Store Submission](bluu-app-store-submission.md) 38 | 39 | ``` 40 | title: Bluu: App Store Submission 41 | tags: Bluu, app, app store submission, privacy policy, D-U-N-S Number, iubenda, screenshots 42 | date: September 15, 2014 43 | slug: bluu-app-store-submission 44 | excerpt: Delays during Apple App Store submission. 45 | author: Andy Pai 46 | ``` 47 | 48 | [Bluu: TestFlight vs. Crashlytics vs. HockeyApp](bluu-testflight-crashlytics-hockeyapp.md) 49 | 50 | ``` 51 | title: Bluu: TestFlight vs. Crashlytics vs. HockeyApp 52 | tags: TestFlight, Crashlytics, Hockey App, beta testing, crash reporting, bluu 53 | date: August 3, 2014 54 | slug: bluu-testflight-crashlytics-hockeyapp 55 | excerpt: We're happy to be using TestFlight for seamless beta distribution services and Crashlytics for its awesome crash reporting. 56 | author: Andy Pai 57 | ``` 58 | 59 | [The Birth of Bluu](the-birth-of-bluu.md) 60 | 61 | ``` 62 | title: The Birth of Bluu 63 | featured: https://i.cloudup.com/bLfc2mYZpd.png 64 | tags: bluu, app, design, game, iOS, Android 65 | date: August 1, 2014 66 | slug: the-birth-of-bluu 67 | excerpt: We wanted to make a app that could be usable by the end of a weekend. 68 | author: Andy Pai 69 | ``` 70 | 71 | [Bluu: Designing the Rule Structure](bluu-designing-the-rule-structure.md) 72 | 73 | ``` 74 | title: Bluu: The Rules 75 | tags: bluu, app, design, game 76 | date: August 1, 2014 77 | slug: bluu-the-rules 78 | excerpt: Creating the rule logic behind Bluu. 79 | author: Andy Pai 80 | ``` 81 | 82 | [An Indian Beginning](an-indian-beginning.md) 83 | 84 | ``` 85 | title: An Indian Beginning 86 | tags: India, coworking space, Bangalore, Cobalt Bangalore, Taj Lands End 87 | date: July 21, 2014 88 | slug: an-indian-beginning 89 | excerpt: Tales from our India trip. 90 | author: Andy Pai 91 | ``` 92 | -------------------------------------------------------------------------------- /an-indian-beginning.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: An Indian Beginning 3 | tags: India, coworking space, Bangalore, Cobalt Bangalore, Taj Lands End 4 | date: July 21, 2014 5 | slug: an-indian-beginning 6 | excerpt: Tales from our India trip. 7 | author: Andy Pai 8 | ``` 9 | 10 | Since Brian was still finishing up work at GE, Matt and I along with Matt Hogan, our co-founder for [Levered Returns](http://www.leveredreturns.com), took a trip to Bangalore, India to start working on the Levered Returns and [Duo](http://duojs.org/) concepts. We figured it would be less distracting and give us a chance to meet some interesting people. 11 | 12 | Below are some highlights from the trip. 13 | 14 | #### The Taj Welcome 15 | 16 | If you have a few hundred dollars in your pocket, I recommend staying at one of the five-star hotels in India and live like a King for couple days. Pictured below is the welcome you get at check-in. 17 | ![img](https://dl.dropboxusercontent.com/u/2312024/taj-welcome.JPG) 18 | 19 | #### Cricket! 20 | 21 | By far India's biggest sport, it was fun playing some street ball. No, it’s not Indian baseball. 22 | ![img](https://dl.dropboxusercontent.com/u/2312024/cricket.JPG) 23 | 24 | #### Rickshaw. 150 Rupees?! 100 Rupees Maximum 25 | 26 | Our primary mode of transport around was the Rickshaw. No, not the ones where [someone cycles you around](http://en.wikipedia.org/wiki/Rickshaws_in_the_United_States). The Indian kind! I don't think they’re made for three dudes our size though. Also, it's pretty hilarious negotiating with drivers, especially when they see foreigners. 27 | ![img](https://i.cloudup.com/vIvZAvALbZ.jpg) 28 | 29 | #### Cobalt 30 | 31 | Before leaving, we were planning to work at [Jaaga](http://jaaga.in/). When we showed up, we realized the co-working space had moved into someone's apartment instead. After a day of rickshawing around Bangalore, picking each other up, and soul-searching, we finally end the search at [Cobalt](http://www.cobaltblr.com/). If you're looking for a Western style co-working space with nice people, this is it! 32 | 33 | ![img](https://dl.dropboxusercontent.com/u/2312024/cobalt.jpg) 34 | 35 | #### $2 Haircut and Shave 36 | 37 | An awesome perk of living in India is the super cheap haircuts and shave. $2 will buy you a haircut, head massage, and shave. The Indian shaving blade can handle even the toughest hairs like Mueller's hipster 'stache. 38 | 39 | ![img](https://dl.dropboxusercontent.com/u/2312024/indian-haircut.JPG) 40 | 41 | #### Random Street Dance Party 42 | 43 | We heard a bunch of noise outside our hotel room, so we stepped out to analyze the commotion. Soon we ended up dancing and waving flags in the middle of the street. Still not sure what we were celebrating... 44 | 45 | 46 | 47 | If you're thinking about taking your start-up to Bangalore, feel free to e-mail us. We would be happy to give you some tips and share some hard learned lessons. 48 | -------------------------------------------------------------------------------- /bluu-testflight-crashlytics-hockeyapp.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Bluu: TestFlight vs. Crashlytics vs. HockeyApp 3 | tags: TestFlight, Crashlytics, Hockey App, beta testing, crash reporting, Bluu 4 | date: August 3, 2014 5 | slug: bluu-testflight-crashlytics-hockeyapp 6 | excerpt: We're happy to be using TestFlight for seamless beta distribution services and Crashlytics for its awesome crash reporting. 7 | author: Andy Pai 8 | ``` 9 | 10 | At the end of the weekend, we wanted to get Bluu into some hands to start getting feedback. Deployment was surprisingly far more complicated than we had envisioned. 11 | 12 | We had to reach out to our friends and family to get their UDIDs, Apple's unique identifier for iPhones. As expected, none of them had ever heard of a UDID. Some started worrying we were trying to send them some type of phone virus. After some explaining, we had to get them to either: 13 | 14 | 1. Download an app that gave them their UDID or 15 | 2. Plug in their iPhones into their computer to get it for us. 16 | 17 | But the pain didn't stop there. Once we received their UDID's, we had to create a build including the collected UDID's and send it to them for installation via e-mail. Once they received the package, they had to reconnect their phone to iTunes and install the app. Even after these steps, sometimes the app wouldn't install! We needed a better beta testing experience, or we risked losing all our friends. 18 | 19 | The three services we came across that promised to improve our workflow were TestFlight, Crashlytics and HockeyApp. HockeyApp charges $10 / month even for their cheapest plan, so they were out. 20 | 21 | After some research on TestFlight and Crashlytics, our first impressions were: 22 | 23 | ## TestFlight 24 | 25 | - Free 26 | - Collects UDID's by downloading an app on the user's phone. They use the same app to deliver the app for download. No cords necessary. Hallelujah! 27 | - Recently acquired by Apple, so should work seamlessly for iOS stuff. Unfortunately, this also means that they won't support Android anymore, but it did look like we could get crash reporting and detailed usage statistics by integrating with their SDK. 28 | 29 | ## Crashlytics 30 | 31 | - Free? Seriously? Even the Enterprise services? Is this a scam?... Oh, they got acquired by Twitter. Hopefully Twitter's deep pockets can probably afford to keep it around and free, at least for a while :) 32 | - Offers beta distribution for iOS and Android 33 | - Crash reporting seems more visual and intuitive than TestFlight 34 | 35 | After trying out Crashlytics and TestFlight, we decided to use both. TestFlight no longer allows downloads of their SDK, so you don't get session tracking, crash logging, and in-app updates. While Crashlytics offers a complete solution including Beta distribution, TestFlight's beta distribution is far more seamless and less buggy. Having to use Crashlytics for crash reporting isn't all a loss though since it's reporting format is superior to TestFlight's format. Crashlytics presents all data with amazing visuals making it very easy to interpret and act accordingly. 36 | 37 | So, we're happy to be using TestFlight for seamless beta distribution services and Crashlytics for its awesome crash reporting. 38 | -------------------------------------------------------------------------------- /bluu-app-store-submission.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Bluu: App Store Submission 3 | tags: Bluu, app, app store submission, privacy policy, D-U-N-S Number, iubenda, screenshots 4 | date: September 15, 2014 5 | slug: bluu-app-store-submission 6 | excerpt: Delays during Apple App Store submission. 7 | author: Andy Pai 8 | ``` 9 | 10 | ### Bluu: App Store Submission 11 | 12 | During the Apple App Store submission process, we were delayed by these three unforeseen requirements: 13 | 14 | 1. D-U-N-S Number 15 | 2. Privacy Policy 16 | 3. Primary Screenshot 17 | 18 | #### D-U-N-S Number (DUNS) 19 | When we were submitting Bluu, we reached a screen that asked for our company's DUNS number. As it turns out, it's easier to get Obama's cell phone number than a DUNS number. 20 | 21 | Basically, no start-up has a DUNS number; you have to acquire it through Dun & Bradstreet. Apple uses this number to verify your identity with Dun & Bradstreet. We tried to get our DUNS number in the following ways: 22 | 23 | 1. Using the D&B website 24 | 2. Using Apple's website 25 | 26 | ##### The Bad Way: Using the D&B website 27 | 28 | This approach is a complete disaster and will make you question if you even want to submit your app. We first tried to get our DUNS number using the tool on [D&B iUpdate](https://iupdate.dnb.com/). After battling with their website that provides no error messages but just refuses to move forward when you hit continue, we eventually decided just to call them. The rep on the phone said we would have to pay around $600 to get our DUNS number instantly or we would have to wait 30-45 days. Ugh...seems like D&B is trying to innovate the art of phone pick pocketing. 29 | 30 | ##### The Less Bad Way: Using Apple's website 31 | 32 | Out of frustration, we then tried to use Apple's [DUNS Number Look-up Tool for Company Enrollment](https://developer.apple.com/ios/enroll/dunsLookupForm.action). After the look-up had failed, it generated a link to submit a request for our DUNS number to D&B on our behalf. Seven days later we had our number. 33 | 34 | 35 | #### Privacy Policy 36 | 37 | App Store submission also requires a privacy policy under certain circumstances. Since Bluu doesn't collect much personal information we used [this](https://dl.dropboxusercontent.com/u/2312024/Bluu%20Privacy%20Policy.pdf) simple privacy policy we got from [iubenda](http://iubenda.refr.cc/47N4N5B), It's a pretty sleek web app that lets you customize your privacy policy depending on the services used in your application (Facebook, Admob, HockeyApp etc.). Use this [link](http://iubenda.refr.cc/47N4N5B) to get 10% off. 38 | 39 | #### Primary Screenshot 40 | 41 | When we were uploading screenshots for Bluu, we realized it would be more effective if the screenshot that shows up in search results looked more like an advertisement. So we created this screenshot: 42 | 43 | ![img](https://dl.dropboxusercontent.com/u/2312024/Bluu-Cover.jpg) 44 | 45 | It took a bit to find the all the iPhone and iPad templates to make the screenshot possible so I'm sharing them for your convenience: 46 | 47 | 1. [iPhone 4](https://dl.dropboxusercontent.com/u/2312024/iPhone4.png) 48 | 2. [iPhone 5](https://dl.dropboxusercontent.com/u/2312024/iPhone5.png) 49 | 3. [iPad](https://dl.dropboxusercontent.com/u/2312024/iPad.png) 50 | 51 | Also, here are the dimensions you need for all the screenshots: 52 | 53 | 1. iPhone 4: 960 x 640 54 | 2. iPhone 5: 1136 x 640 55 | 3. iPhone 6: 1334 x 750 56 | 4. iPhone 6 Plus: 2208 x 1242 57 | 5. iPad: 1536 x 2048 58 | 59 | The iPhone 6 Plus is the strange one since it doesn't match the actual pixel specification on the Apple website. The iPhone 6 Plus basically scales it down to 1920 x 1080, the actual pixel dimensions. 60 | 61 | Hope these tips help make your first submission process easier! 62 | -------------------------------------------------------------------------------- /bluu-the-rules.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Bluu: The Rules 3 | tags: [Bluu, app, design, game] 4 | date: August 1, 2014 5 | slug: bluu-the-rules 6 | excerpt: Creating the rule logic behind Bluu. 7 | author: Andy Pai 8 | ``` 9 | If you didn't read my last post on [The Birth of Bluu](http://lapwinglabs.com/blog/the-birth-of-bluu), here's what you missed: we wanted to give ourselves a crash course in turning a new idea into a released app so that we'd have a better understanding of what it takes, and we figured a simple game would make for a good pilot project. This post talks about how we refined the idea for [Bluu](https://itunes.apple.com/us/app/bluu/id916926135?ls=1&mt=8) into something that we thought would be fun to play and easy to implement. 10 | 11 | The idea behind Bluu was that we could take advantage of the [Stroop Effect](http://en.wikipedia.org/wiki/Stroop_effect) (depicted below) to create a game that was mentally stimulating but simple to implement. 12 | 13 | ![stroop effect](https://i.cloudup.com/6V0mxEDxpu.png) 14 | 15 | But what would that game look like? How would you play it? Well, defining the actual rules for Bluu turned out to be the most time-consuming part of the weekend and required a few iterations. 16 | 17 | ## Keeping it simple 18 | The goal of this project was to learn the intricacies of the overall app development lifecycle, not get bogged down in implementation effort, so we designed Bluu as a really simple 'race-the-clock' game that required players to quickly respond to 'text/color' challenges (whatever that meant...we hadn't really defined it at that point). 19 | 20 | To start, we laid out the gameboard of Bluu with a center ('Inside') tile surrounded by four 'Outside' tiles such that the goal of the game became to match the Inside tile with the correct Outside tile. Kind of an arbitrary decision, but we needed to start somewhere! 21 | 22 | ![img](https://dl.dropboxusercontent.com/u/13957782/Main.png) 23 | 24 | This left us with the task of defining the rules for determining which 'Outside' tile would correctly match an 'Inside' tile. 25 | 26 | ## Matchmaker, Matchmaker... 27 | The mockup above shows three primary characteristics of each tile that we could manipulate for each turn: Background Color, Word Color, and Word. This gave us a whole host of options for rule sets that would make two tiles 'matchable'. We tried a few different combinations, but they all followed the theme of doing things one way unless the color 'blue' was involved. This twist allowed us to add some difficulty to the game without significantly complicating the ruleset because it forces you to think critically at every turn. 28 | 29 | ### Attempt #1 30 | #### Rule 1: Match the background color of the inside tile to the correct word outside. 31 | For example, in the mockup above you would flick the tile down, since its color is red and the bottom tile contains the word 'Red'. 32 | 33 | #### Rule 2: Unless the tile is blue! 34 | If the inside tile is colored blue, then match it to the outside blue tile (so you're matching background colors in this case). 35 | 36 | We realized this rule logic led to a lot of eye movement since you had to read the text on all four outside tiles at all times. So we decided it would be better if *only* the center tile contained any text that required reading. 37 | 38 | ### Attempt #2 39 | #### Rule 1: Match the inside tile's word to the outside background color. 40 | So for example, if the inside tile contains the word "Yellow", you'd find the outer tile that was colored yellow and match with that. This required less eye movement and allowed us to speed up the game. 41 | 42 | #### Rule 2: Unless the tile is blue! 43 | This rule didn't change. If the inside tile is colored blue, then match it to the outside blue tile. 44 | 45 | We played the game internally with this ruleset, and while it was certainly an improvement over having to read 5 tiles per turn, we thought it became too easy after playing a few times. So we shuffled the rules around one more time before introducing to beta testers. 46 | 47 | ### Attempt #3: Beta Testing Rules 48 | #### Rule 1: Match the inside tile's text color to the outside background color... 49 | So if the word in the middle tile looked like Green, you'd send it to the outer tile whose background color was red. This added a little more complexity to the last revision of Rule 1 and more actively introduced the Stroop Effect by forcing you to acknowledge the color a word is printed in, even though it conflicts with the actual name presented. 50 | 51 | #### Rule 2: Unless the text is "blue"... 52 | If the inside tile's text says blue, then you match it to the outside tile's background color. So 'Blue' on the inside tile would match the outside tile colored blue. This rule keeps the game from getting too easy; without it you could just tune out the text entirely. 53 | 54 | #### Rule 3: Or you see blue 55 | If the inside tile's text *color* is blue, then DO NOT match it to the blue outside tile! You can send it to any outer tile that's *not* colored blue. Again, this rule just adds a degree of difficulty and forces you to do some more complicated mental gymnastics before making the right match. 56 | 57 | We released our beta version with these rules, anticipating that Rule #3 may overly-complicate the game. Our testers agreed, commenting that the rules were difficult to keep straight. We even got feedback from some saying that the game was broken because the rules were commonly misunderstood. So for launch, we stuck with only the first two rules from Attempt #3, dropping the 3rd for the sake of simplicity. We also added the tutorial cards that interactively teach you how to play and give you a chance to practice without time constraints so that you can test your understanding. You'll see these the first time you play the game. 58 | 59 | If the game has app store success, it'll be fun remixing the rule combinations for challenge modes and additional levels! 60 | 61 | In [Bluu: TestFlight vs. Crashlytics vs. HockeyApp](http://lapwinglabs.com/blog/bluu-testflight-crashlytics-hockeyapp) we describe our experience with selecting a beta testing and crash reporting service. 62 | -------------------------------------------------------------------------------- /say-hello-to-gittask.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Say Hello to Gittask 3 | tags: [gittask, site, open source, development] 4 | date: January 16, 2017 5 | slug: say-hello-to-gittask 6 | excerpt: Introducing a new marketplace for coding tasks 7 | author: Lapwing Labs 8 | draft: true 9 | ``` 10 | 11 | Gittask is a marketplace for connecting people that need to get coding tasks done with developers who can complete these tasks. 12 | 13 | ![picture of the service](https://cldup.com/v8Bo2umQmy.png) 14 | 15 | You can use Gittask for building your website, adding a feature 16 | 17 | Tasks may include things like: 18 | 19 | - I have a design for a website, I need someone to help write the HTML and CSS. 20 | 21 | - I need someone to help me add Google Maps into my Wordpress website. 22 | 23 | - Help! I have a broken PHP site and I need someone to fix it for me. 24 | 25 | - I need some advice for picking the best charting library 26 | 27 | You can find us here: https://gittask.com 28 | 29 | ## Why we built Gittask 30 | 31 | Developers: We want a world where you can spend a few hours on the computer and earn some money. 32 | 33 | Companies: We want a world where companies can get high quality code written for them in a matter of hours instead of weeks. 34 | 35 | Open Source Authors: to make "Open Source Developer" a job title and a sustainable career. We also want to make working in open source more accessible for a larger group of people who otherwise cannot afford to give their work away for free. 36 | 37 | ---- 38 | 39 | As a developer I want to be able to log into my computer and earn money at a time of my choosing from a location of my choosing. 40 | 41 | As a founder, I want to be able to find people that are experts in certain parts of my stack, that can build those parts of my business better than I can. 42 | 43 | As an open source author, getting paid to build what you love is an ultimate form of freedom. 44 | 45 | ## Gittask for Developers 46 | 47 | We built gittask to help developers quickly earn money during the hours of their choosing, from a location of their choosing. 48 | 49 | Tasks are specific to libraries and languages, so it's easy to find the perfect match for your skill set and start earning money quickly. 50 | 51 | ## Gittask for Companies 52 | 53 | We think companies are going to love Gittask because they can reach out to specialists or even creators of these libraries or languages for help or assistance. 54 | 55 | There’s no risk to getting started with Gittask. It’s free to sign up and free to post a task. Gittask takes care of the marketing for you by automatically posting tasks on Github, Twitter and various other marketing channels. This frees you up to work on other things and reduces your recruiting costs. 56 | 57 | By outsourcing tasks to those who really know these projects well, companies can operate more efficiently, saving time and money. 58 | 59 | For example, if you’re building a website and you need to integrate with Facebook, instead of spending a couple days figuring out the Facebook API, you can just post a task on Gittask to have an expert implement it for you. 60 | 61 | ## Gittask for Open Source 62 | 63 | We believe that open source is fundamentally changing how we write software. 64 | … 65 | Gittask was built on the shoulders of the open source community. 66 | … 67 | That’s why we are giving 10% of each completed task back to the open source community. 68 | 69 | This 10% is distributed based on your contributions to the codebase, so if someone completes a $300 task using jQuery, $30 goes to the top contributors of jQuery. Here’s what the breakdown looks like: 70 | 71 | ![breakdown](https://cldup.com/MctMAFDirt.png) 72 | 73 | You can check the pricing breakdown of your own repositories here: https://gittask.com/pricing 74 | 75 | ## The Landscape 76 | 77 | There are a couple of other services already out there doing similar things, so I want to talk about where we fit into the space. 78 | 79 | ### Bountysource 80 | 81 | In a sentence, Bountysource "builds in" open source, while Gittask "builds on" open source. 82 | 83 | Bountysource supports the open source community through paying for issues to be fixed and features to be added. If there's a lingering bug in jQuery library that's really bothering me, I'll post a bounty to sweeten the deal for whoever takes the time to fix it. 84 | 85 | Gittask supports the open source community by leveraging a developer's expertise in an open source library or language. If I need a custom jQuery carousel for my website, I'd look to Gittask for finding a jQuery expert to get it done quickly and properly. 86 | 87 | ### Gratipay 88 | 89 | In a sentence, Gratipay “donates” to open source, while Gittask “pays back” open source. 90 | 91 | Formerly known as Gittip, [Gratipay](https://gratipay.com) promotes open source projects and businesses through anonymous donations. This gives people an opportunity to help support individuals working on cool projects or interesting businesses. If you’re getting a lot of benefit out of the tools that [TJ Holowaychuk](https://github.com/tj) built, you might consider tipping him for his hard work. 92 | 93 | With Gittask, you pay for the developer’s domain expertise, code, and support. You get something back 94 | 95 | ### oDesk / Elance / Freelancer.com 96 | 97 | oDesk, Elance, and Freelancer.com are the more traditional companies that you’d look to for your outsourcing needs. For [Lapwing Labs](http://lapwinglabs.com), we used each of these services extensively to build [Levered Returns](http://leveredreturns.com). 98 | 99 | We found that the quality on these services was very hit or miss. Often times we had to throw away or refactor the task to make it work in our application. 100 | 101 | We can do better. After working in open source for 4 years, I believe that the open source community has the most talented and passionate developers in the world. By forging a close relationship with the community we will raise the bar and make outsourcing viable for those who are not willing to compromise on quality. 102 | 103 | ## We need your Help 104 | 105 | You can help us. We’re a young company with a lot to learn. 106 | 107 | We’re . We’re bootstrapping gittask with our own money 108 | 109 | 110 | 111 | If you like this product, we’d love some twitter love. 112 | 113 | If you’re an open source author, you can promote tasks on your repositories by adding badges to your Readme. Here’s the code to do that: 114 | 115 | ``` 116 | [https://gittask.com/cheeriojs/cheerio.svg] 117 | ``` 118 | 119 | -------------------------------------------------------------------------------- /principles-of-an-ideal-database-client.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Principles of an Ideal Database Client 3 | tags: [database, client, open source, development] 4 | date: January 25, 2015 5 | slug: principles-of-an-ideal-database-client 6 | excerpt: Discussing the principles that go into an ideal database client 7 | author: Matthew Mueller 8 | ``` 9 | 10 | As we've been building [Gittask](https://gittask.com), we've noticed some very leaky abstractions, specifically around our database client. We've had to write some gnarley boilerplate to handle type conversations and rollbacks. That got me thinking a lot about this question: 11 | 12 | > What would the ideal database client would look like? 13 | 14 | Database clients come in all shapes and sizes. Some are [awful](https://github.com/mongodb/node-mongodb-native) to work with, some are quite [lovely](https://github.com/pebble/yieldb) to work with. Unfortunately, I've found that all database clients fall short in some way or another. Here's what I believe goes into an ideal database client. 15 | 16 | ## Principles of an Ideal Database Client 17 | 18 | In my mind, the ideal database client would have the following characteristics: 19 | 20 | - **Lossless Serialization & Deserialization:** 21 | 22 | The data that goes in, should be exactly the same as the data that comes out. If this is not possible, the data should not go in at all. 23 | 24 | - **Polyglot Persistence:** 25 | 26 | Your database client should be able to speak to different backend databases. 27 | 28 | - **Atomic Transactions across Databases:** 29 | 30 | Your database client should be able to chain together writes across databases and rollback if there's a failure anywhere in the pipeline. 31 | 32 | Let's cover each of these principles in more detail: 33 | 34 | ### Lossless Serialization & Deserialization 35 | 36 | As developers, we shouldn't have to worry about how the database is going to muddle with our data. That's a leaky abstraction and extremely error-prone. 37 | 38 | Our database or database client should be responsible for keeping these data structures intact. When you save a Date object, you should expect to get a Date object back out. 39 | 40 | The serialization and deserialization steps should be separate but consumed by the ideal database client. There may be cases when you do not use this client for data retrieval, but you will still want the data to be cast properly. 41 | 42 | I wrote [superjson](https://github.com/lapwinglabs/superjson) as a first attempt at solving this problem for Node.js. 43 | 44 | ### Polyglot Persistence 45 | 46 | The database landscape has grown immensely over the last couple years. Each new database has certain advantages over the others, but all make certain tradeoffs. 47 | 48 | In fact, the [CAP theorem](http://en.wikipedia.org/wiki/CAP_theorem) tells us that it's impossible to have the perfect database and we need to choose which tradeoffs are acceptable for our application. 49 | 50 | One workaround for these restrains is the idea of [Polygot Persistence](http://martinfowler.com/bliki/PolyglotPersistence.html) that was popularized by [Martin Fowler](https://twitter.com/martinfowler). Polygot Persistence is the idea that you should pick the right database for the right job. This way we have can our cake and eat it too. 51 | 52 | --- 53 | 54 | Our database client should follow suit. Our database client should be able to speak the language of many different databases. In code that might look like this: 55 | 56 | ```js 57 | Client(mongo(details)).put(key, value); 58 | Client(redis(details)).get(key); 59 | ``` 60 | 61 | ### Atomic Transactions 62 | 63 | You should be able to define atomic transactions across databases. 64 | Many databases have a way to do atomic transactions for their respective database, but this is not good enough in the world of polyglot persistence. 65 | 66 | For example, when you create a user, usually you perform (at least) two database writes: 67 | 68 | 1. Save the user data 69 | 2. Save the user session (so they're logged in) 70 | 71 | You could use the same database for both, but then you are sacrificing on either performance or query flexibility. A common polyglot persistence pairing for this task is Mongo for saving user data and Redis for saving the user session. 72 | 73 | It's important that these writes are atomic. If one of these writes fails, your application will be in an invalid state, so we need a way for the database client to "rollback" changes if there's an error while running these commands. The simplest way to do this is to take a snapshot and if there's a failure, rollback to the old values. In code this may look something like this: 74 | 75 | ```js 76 | client.atomic() 77 | .db(mongo).put('user', obj) 78 | .db(redis).put('session:' + sid, obj.id) 79 | .run(fn); 80 | ``` 81 | 82 | Before each write, the ideal client would know how to reverse itself in the event of an error somewhere in the pipeline. 83 | 84 | ## What about ORM? 85 | 86 | I spent a lot of time [using](https://github.com/LearnBoost/mongoose/) and [building](https://github.com/modella/modella) ORMs and I'm now convinced that they are leaky abstraction and do more harm than good as your application grows. 87 | 88 | Laurie Voss blogged back in 2011 about why [ORM is an anti-pattern](http://seldo.com/weblog/2011/08/11/orm_is_an_antipattern). This blog post is still very relevant. 89 | 90 | I think you can get most of the advantages an ORM offers by having a good standalone data validation library (I recommend [rube](http://github.com/lapwinglabs/rube)) and our ideal database client. 91 | 92 | Then you can write your own models in a performant way with the best tools for the job. 93 | 94 | ## What about the unique features that databases provide? 95 | 96 | You may be wondering: well, some databases have more features than others. How do you write a client that can still take advantage of a database's unique features? 97 | 98 | I believe all databases operations can be boiled down to a few low-level operations: 99 | 100 | ``` 101 | - client.get(key) 102 | - client.put(key, value) 103 | - client.del(key) 104 | - client.select(collection) (or "database", "table", "sublevel", etc.) 105 | - client.atomic() (initialize an atomic transaction) 106 | ``` 107 | 108 | I think the ideal database would natively support these operations, but be extended for a database's unique features (via signals). In code it may look something like this: 109 | 110 | ```js 111 | var client = Client(mongo(details)); 112 | client.put(key, value); 113 | client.mongo.query(query) 114 | ``` 115 | 116 | The reason for `client.mongo` is that it's now very easy to locate those features which are unique to mongo. This makes your databases more interchangeable. So if we change our underlying database to LevelDB, our mongo-specific queries will throw: 117 | 118 | ```js 119 | var client = Client(leveldb(details)); 120 | client.put(key, value); 121 | client.mongo.query(query); 122 | ``` 123 | 124 | ## Let's build this together 125 | 126 | The ideal database has not been built yet. Right now, it's a series of high-level ideas that should go into building this client. I'm calling on the community to help get involved to hammer out the details and harden the implementation. 127 | 128 | If you are interested in getting involved or following along, I've set up a repository on [Github](https://github.com/lapwinglabs/yurt), with the working title Yurt. 129 | 130 | As always, if you like the work we do or want to follow us along as we learn, follow us on Twitter at [@lapwinglabs](https://twitter.com/lapwinglabs). 131 | -------------------------------------------------------------------------------- /hacker-guide-to-setting-up-your-mac.md: -------------------------------------------------------------------------------- 1 | ``` 2 | title: Hacker's Guide to Setting up Your Mac 3 | tags: [mac, hacker, bash] 4 | date: September 30, 2014 5 | slug: hacker-guide-to-setting-up-your-mac 6 | excerpt: Setting up your Mac the Hacker way 7 | author: Matthew Mueller 8 | ``` 9 | 10 | Hackers obsess over automation. We want robots to do the grunt work so we can focus on the fun stuff. One area that's ripe for automation that hasn't seen much attention lately is setting up your computer. 11 | 12 | Today I want to show you some techniques to apply automation to the setup of your Mac. The goal of this post is to automate 80% of the bootstrapping, allowing you to setup a new Mac in a matter of hours, not days. 13 | 14 | ![terminal](https://raw.githubusercontent.com/lapwinglabs/blog/master/assets/hacker-guide-to-setting-up-your-mac/terminal.png) 15 | 16 | ## Previous Work 17 | 18 | There has been previous work done in this area to automate your Mac's setup. [Boxen](https://boxen.github.com/) is probably the most notable. Boxen is Github's solution to keeping their teams running similar environments so there aren't as many inconsistencies across boxes. Boxen is a great solution for more mature companies with devops teams 19 | , but what about the small startups or the lone hackers? We need a more suitable solution for them. 20 | 21 | ## Our toolbox 22 | 23 | This blog post will make use of the following open source tools to automate your Mac: 24 | 25 | - Installing Binaries with [homebrew](http://brew.sh/) 26 | - Installing Apps with [homebrew cask](http://caskroom.io/) 27 | - Backing up and Restoring Configuration with [mackup](https://github.com/lra/mackup) 28 | - Solid Mac defaults for hackers using [osx-for-hackers.sh](https://gist.github.com/brandonb927/3195465) (modified) 29 | - Bringing it all together with [dots](https://github.com/matthewmueller/dots) 30 | 31 | ## Installing Binaries with Homebrew 32 | 33 | Homebrew is a community-driven package installer and an essential tool for every hacker's toolkit. Homebrew automates the setup, compiling and linking of binaries. It also makes updating and uninstalling binaries a breeze. 34 | 35 | This is the first thing you should install on a fresh mac. Drop this snippet in a bash script to make sure homebrew gets installed: 36 | 37 | ```bash 38 | # Check for Homebrew, 39 | # Install if we don't have it 40 | if test ! $(which brew); then 41 | echo "Installing homebrew..." 42 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 43 | fi 44 | 45 | # Update homebrew recipes 46 | brew update 47 | ``` 48 | 49 | The next thing you should do is update the unix tools you already have on your mac. This is more relevant than ever since the recent ["Shellshock"](http://goo.gl/zElPKk) debacle. 50 | 51 | Here's a snippet to update these unix tools: 52 | 53 | ```bash 54 | # Install GNU core utilities (those that come with OS X are outdated) 55 | brew install coreutils 56 | 57 | # Install GNU `find`, `locate`, `updatedb`, and `xargs`, g-prefixed 58 | brew install findutils 59 | 60 | # Install Bash 4 61 | brew install bash 62 | 63 | # Install more recent versions of some OS X tools 64 | brew tap homebrew/dupes 65 | brew install homebrew/dupes/grep 66 | ``` 67 | 68 | You'll also need to update the `$PATH` in your `~/.bash_profile` in order to use these tools over their Mac counterparts: 69 | 70 | ```bash 71 | $PATH=$(brew --prefix coreutils)/libexec/gnubin:$PATH 72 | ``` 73 | 74 | This establishes a solid foundation for your Mac. You can also install other tools with Homebrew to improve your workflow. Here's what I install: 75 | 76 | ```bash 77 | binaries=( 78 | graphicsmagick 79 | webkit2png 80 | rename 81 | zopfli 82 | ffmpeg 83 | python 84 | sshfs 85 | trash 86 | node 87 | tree 88 | ack 89 | hub 90 | git 91 | ) 92 | 93 | echo "installing binaries..." 94 | brew install ${binaries[@]} 95 | ``` 96 | 97 | After you're done, you should clean everything up with: 98 | 99 | ```bash 100 | brew cleanup 101 | ``` 102 | 103 | ## Installing Apps with Homebrew Cask 104 | 105 | [Homebrew Cask](http://caskroom.io/) is an extension for Homebrew that allows you to automate the installation of Mac Apps and Fonts. 106 | 107 | After you have homebrew installed, you'll want to install Homebrew Cask: 108 | 109 | ```bash 110 | brew install caskroom/cask/brew-cask 111 | ``` 112 | 113 | The number of apps you can install with Cask is enormous and growing every day. You can take a look at what applications are installable in their [caskroom/homebrew-cask](https://github.com/caskroom/homebrew-cask/tree/master/Casks) repo or you can search for applications from the CLI: 114 | 115 | ```bash 116 | brew cask search /google-chrome/ 117 | ``` 118 | 119 | Everyone's choice of apps will be different, but here is the script I use to install my favorite apps: 120 | 121 | ```bash 122 | # Apps 123 | apps=( 124 | alfred 125 | dropbox 126 | google-chrome 127 | qlcolorcode 128 | screenflick 129 | slack 130 | transmit 131 | appcleaner 132 | firefox 133 | hazel 134 | qlmarkdown 135 | seil 136 | spotify 137 | vagrant 138 | arq 139 | flash 140 | iterm2 141 | qlprettypatch 142 | shiori 143 | sublime-text3 144 | virtualbox 145 | atom 146 | flux 147 | mailbox 148 | qlstephen 149 | sketch 150 | tower 151 | vlc 152 | cloudup 153 | nvalt 154 | quicklook-json 155 | skype 156 | transmission 157 | ) 158 | 159 | # Install apps to /Applications 160 | # Default is: /Users/$user/Applications 161 | echo "installing apps..." 162 | brew cask install --appdir="/Applications" ${apps[@]} 163 | ``` 164 | 165 | If you want to install beta versions of things like Chrome Canary or Sublime Text 3, you'll need to tap the `versions` cask: 166 | 167 | ```bash 168 | brew tap caskroom/versions 169 | ``` 170 | 171 | ### Attention Alfred users 172 | 173 | One thing you may notice if you're an [Alfred](http://www.alfredapp.com/) user is that you cannot actually launch these apps from Alfred because the actual location of the app is not in `/Applications` but in `/opt/homebrew-cask/Caskroom/`. 174 | 175 | To add this path to Alfred, you can run the following command: 176 | 177 | ```bash 178 | brew cask alfred link 179 | ``` 180 | 181 | Voila! 182 | 183 | ### Bonus: Installing Fonts like a Boss 184 | 185 | Cask can also be used to automatically download and install fonts. In order to enable this, you'll need to tap the `fonts` cask: 186 | 187 | ```bash 188 | brew tap caskroom/fonts 189 | ``` 190 | 191 | The font recipes are prefixed by `font-*`, so if you want to download [Roboto](http://www.google.com/fonts/specimen/Roboto), try searching for `font-roboto`: 192 | 193 | ```bash 194 | brew cask search /font-roboto/ 195 | ``` 196 | 197 | Here's how I install fonts: 198 | 199 | ```bash 200 | # fonts 201 | fonts=( 202 | font-m-plus 203 | font-clear-sans 204 | font-roboto 205 | ) 206 | 207 | # install fonts 208 | echo "installing fonts..." 209 | brew cask install ${fonts[@]} 210 | ``` 211 | 212 | You can find a full list of the fonts in the [caskroom/homebrew-fonts](https://github.com/caskroom/homebrew-fonts/tree/master/Casks) repo. 213 | 214 | ## Mackup 215 | 216 | [Mackup](https://github.com/lra/mackup) is a community-driven tool for backing up and restoring system and application settings. You can find the list of applications it supports in the [lra/mackup](https://github.com/lra/mackup/tree/master/mackup/applications) repo. 217 | 218 | I haven't had much luck installing Mackup using Homebrew (on Yosemite), but it's easy enough to install with python's `pip`: 219 | 220 | ```bash 221 | pip install mackup 222 | ``` 223 | 224 | If `pip` is not available, you may need to install `python` with `brew install python`. 225 | 226 | By default mackup saves your preferences to your Dropbox, so you'll want to setup Dropbox first. Once Dropbox is setup, backing up your settings is simple: 227 | 228 | ```bash 229 | mackup backup 230 | ``` 231 | 232 | This command will match your installed applications with it's recipes and symlink the settings files to `~/Dropbox/Mackup`. 233 | 234 | To restore these settings on another Mac or a wiped Mac, simply run: 235 | 236 | ```bash 237 | mackup restore 238 | ``` 239 | 240 | ## osx-for-hackers.sh 241 | 242 | [osx-for-hackers.sh](https://gist.github.com/brandonb927/3195465) is a script by [Brandon Brown](https://github.com/brandonb927) that is based on [Mathias Bynens](https://github.com/mathiasbynens)'s popular [dotfiles](https://github.com/mathiasbynens/dotfiles/blob/master/.osx). 243 | 244 | This script optimizes your Mac's settings for hackability. It disables many of the annoying defaults Macs have, speeds up the keyboard repeat rate and window animations, and applies many other tweaks. 245 | 246 | This script should not be run without prior examination. It's quite opinionated and intended to be modified. You can find the version I modified here: 247 | 248 | https://gist.github.com/MatthewMueller/e22d9840f9ea2fee4716 249 | 250 | This version makes the script more idempotent, removing a lot of the prompts that I'd like to handle in other places. 251 | 252 | ## dots(1) 253 | 254 | [dots(1)](https://github.com/matthewmueller/dots) is a script I wrote to glue these concepts together. It's intended to be the first thing you install on your Mac (or Ubuntu server). It has no outside dependencies and works on many different distributions. To get the binary, simply run: 255 | 256 | ``` 257 | (mkdir -p /tmp/dots && cd /tmp/dots && curl -L# https://github.com/matthewmueller/dots/archive/master.tar.gz | tar zx --strip 1 && sh ./install.sh) 258 | ``` 259 | 260 | To boot up your Mac with sensible defaults, you can run: 261 | 262 | ```bash 263 | dots boot osx 264 | ``` 265 | 266 | [dots(1)](https://github.com/matthewmueller/dots) is very much a work in progress, but I'm hoping to align the community's efforts around creating robust tools to quickly bootstrap new hacker-friendly machines. 267 | 268 | ## Conclusion 269 | 270 | By setting up automation, you can get up and running on a new Mac faster, you will stay up to date with the latest security fixes and you can minimize inconsistencies among your teammate's computers. 271 | 272 | What are your favorite tools for automation? Leave a comment! 273 | 274 | If you're interested in this kind of stuff or in our [other work](http://lapwinglabs.com/#work), you should [get in touch](mailto:hi@lapwinglabs.com). 275 | 276 | Happy automating! 277 | --------------------------------------------------------------------------------