David Heinemeier Hansson on why Writing software is hard

Links

David Heinemeier Hansson:

Good software is uncommon because writing it is hard. In the abstract, we all know that it is hard. We talk incessantly about how it’s hard. And yet, we also collectively seem shocked — just shocked! — when the expectable happens and the software we’re exposed to or is working on turns out poor.

This is classic cognitive dissonance: Accepting that writing software is hard, but expecting that all of it should be good.

It’s also an application of a just-world theory of effort and accomplishment. That despite the odds, everyone who has the right intent at heart, and puts in the work, will succeed. No they won’t. That’s just delusional. And those delusions are anything but harmless. When we expect good software to be the most likely outcome from the hardship of writing it, we’re setting ourselves up for inevitable disappointment. Even worse, if we feel we deserve good software from our imperfect efforts, we’ll project the inevitable failures on everyone and everything but ourselves.

Another good read from DHH.

We actually treat all of our code as if someone else will use it to develop or learn from at some point, so we try to follow a scheme to keep it easy to read and jump into. That’s been our policy from before Flybase and continues to this day.


Wishing you and yours a Merry Christmas and a Happy New Year

Inside

As another year comes to a close, we wanted to wish everyone a Merry Christmas and a Happy New Year in 2017.

As we enter our third year of operation, we like to reflect back on the previous years and think about what we’ve done, and what we have planned for the new year and it makes us happy to think of all we’re bringing to you, our users.

We’ve seen a lot of growth as people realize what they can do with Flybase, and as we bring on our nextgen set of features, people will be able to do even more.


Launch mode vs iterate mode

Links , Startups

Paul Adams:

We had a really successful launch, with hundreds of customers now using it in production, but we don’t yet have a successful product. We must obsessively work to understand how our customers are using the product, what is working well, what needs to be improved. We need to talk to many prospective customers or customers on trial and understand what they need us to change for them to adopt the product.

Launching a product, and iterating a product, are two very different things and they require the team to think and operate in very different ways. We have been in launch mode for many months, and it is now critical to change into iterate mode. This is a really hard thing to do because it means breaking all our habits and ways of working and thinking in the team. The ability to do this asap though will define our success over the coming months. The very things that made the launch successful will make us unsuccessful in this new mode.

When we are in launch mode, we are ruthless about scope. We are narrowing and narrowing. Deciding to do anything other than the defined launch list requires much deliberation. “No” is the default answer to new things. We are necessarily risk averse.

Iterating a product is the opposite. We need to be very open minded and try many small things. We need to make changes to the product based on customer feedback, knowing that we may roll back those changes or change them a second or third time. Success for Educate in 2017 means changing much of our product. We ship to learn. We know that the product has a long way to go.

When I came across this post, I wanted to share it as it’s definitely true. We launched Flybase two years ago, and we’ve been enjoying steady growth since and as we iterate with new features, we’re seeing even more growth, but we also remember the important things. Rather than launching a ton of new features at once, it’s easier to launch them in pieces and gauge how well received they are, this helps us focus more on what our users want.


Jason Fried Measures in on what's in an hour

Links

Jason Fried:

When 15 + 15 + 15 + 15 does not equal 60

Remember the first time someone asked you what’s heavier — a pound of feathers or a pound of lead? You probably fell for it. I sure did.

A pound is a pound! Duh!

But the same thing doesn’t apply with an hour. Every hour may be 60 minutes total, but that’s where the similarities end.

[..]

Time is the most precious thing there is, yet we split it up and give it away like there’s an endless supply. And whatever time you do have, you have even less attention.

So… Guard your big rocks. Hoard them. Protect them. Don’t let the job crush them into smaller and smaller pieces. And if you’re a manager, team lead, or owner of a business, make it a top responsibility to protect and preserve your people’s time and attention. Treat it as the most precious resource there is. Because it is.

Good advice as usual from Jason.


Manage Your Data With Indexes

Inside , Code

We’ve mentioned before about our roll out of nextgen features, and we are starting to roll these out now.

We’ve rolled out the queues, and now this week we’ve launched our schema builder.

This is a very basic schema builder, after you’ve added a few records to a collection, you can set rules for each field, such as field types (number, string or mixed), index rules such as unique or required (or unique and required).

This gives you more control over your data, and also cuts down on doing things like searching to see if a record exists before adding it, now you just save it and that was it.

Some other rules will be coming shortly, followed by our new GraphQL API which is shaping up beautifully and is in testing with a few users now. Then we’ll be launching the rest of our nextgen features, but we are rolling these out one by one so that people can play with them as they roll out and not have a ton of new features hit them at once.


Six Steps for Startups to Master the Corporate Minefield

Links

Dr. Nicolai Schättgen, writing for the startup grind:

Collaborations between corporates and startups are difficult and painful. Corporates are slow, can’t decide, are inflexible, have terrible processes and often even show a bad attitude towards startups.

In a recent survey we asked startups about their perception of the corporates – their responses – and my personal favorites were, “cold-war technologies,” “pedantic” or “stodgy.”

Still, startups are well advised to understand that a successful corporate collaboration is generally a critical element of success. Corporates might serve as a buyer, as a sales partner, as a supplier, as a strategic partner or simply as a service provider.

At some point dealing with corporates becomes almost – inevitable.

Thereby startups are well advised to be careful when being critical of the big corporations, especially when reviewing missed collaboration opportunities. Too fast and too often corporates are blamed for anything and everything – as we all know them as “bad.” But I have seen many instances where the real problem was anything but the corporate’s fault.

Startups can significantly increase their success rate, if they adhere to six basic rules. These refer predominantly to the corporate as the buyer, but can also be transferred to many startup-corporate situations.

The corporate minefield is something every startup needs to navigate and Nicolai has covered six of the steps that are especially useful.


Get the most out of your research with storytelling

Links , Startups

The job of research is to distill complex problems so that your team understand the what, the why, and the how of the problem at hand.

Yet when presenting complex-yet-eye-opening data to colleagues, a tidy 10-page report can fail to elicit the right reaction. That’s because when it comes to sharing research with your colleagues, presentation matters.

The right presentation can transform something that sits on your colleague’s desk collecting dust into insights your team can really empathize with. And one of the best ways to present strategic, complex, controversial, or high-volume data is to use storytelling.

We use storytelling a lot in our research, we also use it when planning new features as part of working out where feature A would come into play.


Queuing It Up With Flybase Simple Queues

Code

Queues are handy for processing items in the background, you just send an item to the queue and have a worker in the background to do something with it and remove it from the queue. You can even have multiple workers running

Flybase is great for setting up queues, but we wanted to make it even better (and simpler) so we built our Queue system.

Adding items to a queue is simple, you just have to use the enqueue function, available in our latest javascript and node libraries:

var ref = new Flybase("YOUR-FLYBASE-APIKEY-OR-TOKEN", "web", "posts");
ref.enqueue({
	username: 'Joe',
	message: 'Hello',
	published: new Date().getTime()
});

Then to get items out of a queue, you just dequeue it:

var ref = new Flybase("YOUR-FLYBASE-APIKEY-OR-TOKEN", "web", "posts");

function worker(){
	//	check if there are pending jobs..
	ref.getLength(function(row){
		if( row.jobs ){
			//	grab the first item from the queue and process it...
			ref.dequeue(function( data ){
				console.log( data.username + " said: " + data.message + " on " + data.published);
				worker();
			});
		}else{
			worker();
		}
	});
}
worker();

The getLength function returns a count of pending tasks in your queue, Then the dequeue function returns the next item in your queue. If there are no pending jobs, then it just performs another check to see.

You could run the worker portion of your tasks in a backend node.js app, while the enqueue portion can be handled from anywhere, even direct from the browser.


RESTful Queues

You can also use our REST API to enqueue and dequeue items:

curl -X GET -H "X-Flybase-API-Key: YOUR-FLYBASE-APIKEY-OR-TOKEN" \
	  https://api.flybase.io/queue/web

Will return the next item in the queue.

curl -X GET -H "X-Flybase-API-Key: YOUR-FLYBASE-APIKEY-OR-TOKEN" \
	  https://api.flybase.io/queue/web/count

Will return the number of pending jobs currently in the queue.

And finally:

curl -X POST -d '{ "username": "baileys", "date_of_birth": "June 9, 1978", "full_name": "Bailey Stringer"}' \
	  -H "X-Flybase-API-Key: YOUR-FLYBASE-APIKEY-OR-TOKEN" \
	  https://api.flybase.io/queue/web
			

will store a new task to our queue for your Flybase app called web


Queued items are removed from the queue once retrieved either via the Javascript library or from the REST API. We’ve kept our queue system simple to jump into and use, and you can have as many workers working as you want.

There are various ways you can use the queue system, and we’ve kept it as simple as possible on purpose, we don’t want to overcomplicate something as simple as queues.


How to filter your content in real-time with the Sift Ninja API and Flybase

Code

If you have an app that involves letting users enter content of any kind, regardless if it’s chat, discussion boards, or a social site, you want to have the ability to filter content to make sure it is appropriate.

Sift Ninja provides a handy API that does just that, and combined with Flybase, you can offer real-time content checking before posting it anywhere.


Getting Started

To get started, you’ll need two things:

  1. A Sift Ninja account, sign up here if you don’t already have one.
  2. A Flybase account, sign up here if you don’t already have one.

Setting up Sift Ninja

When you set up your Sift Ninja account, you’ll be asked to create a custom channel, name it whatever you want. This will then give you a unique URL as well as your API key.

Setting up Flybase

Inside your Flybase account, create an app called siftninja, you’ll want that and your API key.

Setting up our Node app.

Our node app is going to be really simple, from your terminal type:

$ npm init
$ npm install --save finalhandler flybase siftninja serve-static

This will install the node modules we need for our app.

The backend

Create a file called index.js, which will contain a Flybase listener to check content entered on our chat page:

var finalhandler = require('finalhandler')
var http = require('http')
var serveStatic = require('serve-static')
var path = require('path');

var flybase = require('flybase');
var flybaseRef = flybase.init('siftninja', "chat", '<YOUR-FLYBASE-API-KEY>');

var apikey = '<YOUR-SIFT-NINJA-API-KEY>';
var url = '<YOUR-SIFT-NINJA-URL>';
var siftninja = require("siftninja")(url, apikey);

flybaseRef.on("check_content", function( req ) {
	var req = JSON.parse( req );
	var message = req.message;
	var sessionKey = req.session;
	siftninja( message ).then(function(result) {
		var result = result.body;
		result.message = message;
		if( result.response ){
			var text = ('' + message).replace( /[<>]/g, '' );
			flybaseRef.push({text:text});
		}else{
			flybaseRef.trigger( "results_"+sessionKey, result );
		}
	});
});

var serve = serveStatic('public', {
	maxAge: '1d',
	'index': ['index.html'],
	fallthrough: true
});

// Create server
var server = http.createServer(function onRequest (req, res) {
	serve(req, res, finalhandler(req, res))
})

// Listen
var port = process.env.PORT || 3000;
server.listen( port, function() {
	console.log('Server started on port ' + port );
});

This will create a custom event listener called check_content, which will check the message for any inapproriate text and return the results back to our frontend app.

If response was true, then the text was ok, and we save it to our Flybase app, and thus let it appear in the chat window.

If the response was false, then there was a problem with the text and we notify the user who entered it and do not save it. We’ll pass a unique session id for the user, which lets us respond to that user without notifying all other users.

The Frontend

Create a folder called public and inside that folder, create a file called index.html:

<html>
<head>
	<title>Flybase and Sift Ninja</title>
	<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
	<br />
	<div class="container well">
		<h2>Enter Chat and press enter</h2>
		<div class="form-group">
			<input type="text" id="input" class="form-control" placeholder="Enter your message">
		</div>
		<hr />
		<h3>Chat Output</h3>
		<ul class="list-group" id="chatbox"></ul>
	</div>
	<script src="https://cdn.flybase.io/flybase.js"></script>
	<script>
		var flybase = new Flybase("<YOUR-FLYBASE-API-KEY>", "siftninja", "chat");

		if (sessionStorage.getItem("sessionKey")) {
			var sessionKey = sessionStorage.getItem("sessionKey");
		}else{
			var sessionKey = uniqueID();
			sessionStorage.setItem("sessionKey", sessionKey);
		}

		function $(id) { return document.getElementById(id); }
		var box = $('chatbox'), input = $('input'), name = $('nameInput');

		input.addEventListener('keyup', function(e) {
			if ((e.keyCode || e.charCode) === 13) {
				flybase.trigger('check_content', {message:input.value,session:sessionKey});
			}
		});

		flybase.on("results_"+sessionKey, function(data){
			if( !data.response ){
				displayChatMessage( '&uarr; <strong>Sift ninja found something bad!</strong>' );
			}
		});

		function displayChatMessage( str ){
			box.innerHTML =  "<li class='list-group-item'>" + str + '</li>' + box.innerHTML;
		}

		flybase.once('value', function (data) {
			if( data.count() ){
				data.forEach( function(message){
					displayChatMessage(message.value().text );
				});
			}
		});

		flybase.on('added', function (message) {
			displayChatMessage( message.value().text );
		});

		function uniqueID(){
			function chr4(){
				return Math.random().toString(16).slice(-4);
			}
			return chr4() + chr4() + '-' + chr4() + '-' + chr4() + '-' + chr4() + '-' + chr4() + chr4() + chr4();
		}
	</script>
</body>
</html>

We’ve kept this app simple, when the user hits the enter key on their text it sends an event to our backend via Flybase and then listens for a reply.

We assign our chat users a unique session ID that follows them for the life of that browser session, this then lets us add a unique listening event called results_<SESSIONKEY> that we can use to pass any issues we’ve found directly back to that user.

Finishing Up

You can see the original repo here.

Feel free to expand on what this can do, as Sift Ninja is a handy tool to have in your arsenal and the guys behind it are solid in this area, and combining it with Flybase’s real-time events lets you check content entered in real-time before saving it to your database or forwarding it to other users.


How About Code Reviews?

Links , Code

Last time, we talked about empathy and what goes into good pull requests. This time, let’s talk about the other side of the equation: what makes a good code review?

Why are we doing this?

First, it’s important to remember why we’re bothering with pull requests and code reviews in the first place.

They offer us an opportunity to share and explain our work.

They give us a place for feedback.

They demonstrate that we are responsibly managing which code we ship to our users.

They give us a chance to teach (by introducing people to a new part of the code) and learn (by receiving feedback).

Each of those reasons is different, but the same strategies are important for all of them.

This post from the Slack Engineering blog was from February but I wanted to share it now as code reviews are a handy thing to do from time to time. You’d be surprised what you can learn from your code when you have other people review it.