How I’m testing seed data generation

When I create a new Rails project I like to have a robust seeds that can be used to quickly bootstrap development, testing and staging environments to interact with the application. I think this is critical for development speed.

If a developer creates a feature to, for example, connect two records together, you just want them to fire up the application and connect two records to see it work. You don’t want them spending time creating the records because that’s a waste of time, but also, because all developers end up having different subsets of testing data and generally ignoring everything that’s not included in them. It’s better to grow a set of testing data that’s good and complete.

One of the problem I run into is that generating testing data, or sample data, doesn’t happen often enough and changes in the application often break it. Because of that, I wrote this simple test:

RSpec.describe "rake db:seed" do
  it "runs" do
    Imok::Application.load_tasks if Rake::Task.tasks.none? { |t| t.name == "db:seed" }
    ENV["verbose"] = "false"
    Rake::Task["db:seed"].invoke
  end
end

It doesn’t have any assertions, but with just a few lines of code it probably covers 90% of seed data creation problems, that generally result in a crash. I would advice against having assertions here, as they may cost more time than the time they’ll save because sample data evolves a lot and it’s not production critical.

The startup CTO dilemma

About 10 years ago I took my first job as CTO but I wasn’t a CTO, I just had the title. I was a developer with ambition. I made mistakes, very expensive mistakes, mistakes that contributed to the failure of the startup. Since then I have learned and grown a lot and although there’s still a lot for me to learn, there are some things I understand reasonably well. One of those is how to be the CTO of an early and not so early stage startup.

With this experience, though, my salary went up. I’m more expensive now than I was 10 years ago and I didn’t know what I was doing. Because of this, I tend to evaluate working for a startup not on day 1, but on day 700 or later, when they have some traction, revenue, etc. The problem is that a lot of those startups are deep in problems that are very hard or impossible to fix by that point. It’s very painful for me to see expenses that cost hundreds of thousands of dollars because someone didn’t do 30 minutes of work 5 years ago (this is a real example).

So, the dilemma is this:

  • If a startup hires an experienced CTO from day 1, they are wasting money because they might only be spending 5% or 10% CTOing and the rest coding, doing IT, etc. which can be done by a less experienced developer.
  • If a startup doesn’t hire an experienced CTO from day 1, they are likely to make very expensive mistakes that may literally kill the startup in year 3 or it may slow it down a lot.

 I’ve been thinking about this for a while, how can this be solved?

One of my ideas was being a sort of CTO enhancer, to be the voice of experience for a less experienced co-founding CTO, helping them a few hours a week for a few months up to a couple of years. What do you think? Does this sound valuable? Useful?

I might be thinking a lot about this lately since I’m leaving my current job and searching for the next thing to do.

Growth vs maintenance time

Someone asked me if I would have a property manager if I lived 5 minutes away from my rental property instead of across the Atlantic from it.

Yes, of course I would!

You have two types of time in your life: Free and Taken. Taken time is the one consumed by tasks required to maintain the current condition of your life. You need a job to pay the bills, you need to take the garbage out to avoid living in toxic waste. This time is, in the long run, a waste, because you get nothing from it other than surviving.

Now, free time is more interesting. You can use it as you want, you can binge on all TV shows or you can use it for growth: learn a new skill, buy a new property, create a new business, work a few extra hours for savings.

Only during free time you can really create the compounding growth that leads to financial independence. If you are not familiar with compounding interest, I’d recommend you read about it, but long story short, small changes in investment now, bring big changes in the future.

Based on that, one should maximize the amount of growth time, even if it costs money, because in the long run, it makes money.

To put it in concrete terms, I’d rather have 1 extra free hour per week to figure out how to buy my new property, than ditch the property manager and make 10% more per month… because an extra property is much more wealth than 10% of extra revenue in one.

My end goal is to reach the point in which I could permanently leave on holidays on a beach and my life would just work, because all responsibilities will have someone assigned to them that gets paid to fulfill them, not because I want to live on permanent holidays, but because I want to live in 100% growth.

One clarification: a job that lets you save money is part maintenance, part growth time.

Nicer printing of Rails models

I like my models to be printed nicely, to make the class of the model as well as the id and other data available, so, when they end up in a log or console, I can now exactly what it is. I’ve been doing this since before Rails 3 and since Rails projects now have an ApplicationRecord class, it’s even easier.

On my global parent for all model classes, ApplicationRecord I add this:

def to_s(extra = nil)
  if extra
    extra = ":#{extra}"
  end
  "#<#{self.class.name}:#{id}#{extra}>"
end

That makes all records automatically print as:

<ModelName:id>

For example:

<User:123>

which makes the record findable.

But also, allows the sub-classes to add a bit of extra information without having to re-specify the whole format. For example, for the User class, it might be:

  def to_s
    super(email)
  end

so that when the user gets printed, it ends up being:

<User:123:sam@example.com>

which I found helps a lot with quickly debugging issues, in production as well as development environments.

Editing Rails 6.0 credentials on Windows

Rails 6 shipped with a very nice feature to keep encrypted credentials on the repo but separate them by environment, so you can have the credentials for development, staging and production, encrypted with different keys, that you keep safe at different levels.

For example, you might give the development key to all developers, but the production keys are kept very secret and only accessible to a small set of devops people.

You edit these credentials by running:

bundle exec rails credentials:edit --environment development

for the development credentials, or

bundle exec rails credentials:edit --environment production

for production ones. You get the idea.

When you run it, if the credentials don’t exist, it generates a key. If they exist, you need to have the keys. After decrypting, it runs your default editor and on Windows, this is the error I was getting:

No $EDITOR to open file in. Assign one like this:

EDITOR="mate --wait" bin/rails credentials:edit

For editors that fork and exit immediately, it's important to pass a wait flag,
otherwise the credentials will be saved immediately with no chance to edit.

It took me a surprisingly long time to figure out how to set the editor on Windows, so, for me and others, I’m documenting it in this post:

$env:EDITOR="notepad"

After that, running the credentials:edit command works and opens Notepad. Not the best editor by far, but for this quick changes, it works. Oh, and I’m using Powershell. I haven’t run cmd in ages.

Talk to each other, you are missing out

For the past few weeks I’ve been talking to entrepreneurs, trying to help with their problems. I’ve heard both of these statements repeated a few times:

Marketing is easy, but coding is impossible.

Marketeers

and

Building a thing is fun, but then I have no idea how to market it.

Coders

It’s frustrating to hear both this things at the same time. Even within each of the communities, inside Indie Hackers, Microconf, TWiST, we seem to have subgroups of techies and non-techies that talk among themselves but not to one another and they are both wondering where the other ones are.

Please: start talking to one another, you need those connections, your idea need those connections.

If you are either a marketer or a coder and want the other one to join your team as a non-paid co-founder you are asking them to make an investment. A huge investment in terms of personal wealth. A person might be able to start 10 startups in their life, so, you are asking them for 1 tenth of their resources. Act accordingly. Put the effort, show traction, show results. I see people put more effort into talking to an investor that will invest only 1% of their resources.

The second thing a lot of us should do, and this include me, is not focus so much on our own ideas and try to work on other people’s ideas. Build someone else’s thing, market someone else’s thing. Don’t chase one idea, chase the outcome of a successful startup and accept that it might not be your idea.

If you are a developer, know this: whatever idea you come up with, it’s an idea that another developer is likely to have, and likely to implement to compete with you. That’s why there’s so much out there in terms of Twitter clones, issue trackers, cryptocurrencies thingies, etc. A non-coder idea has the value of less competition. A CRM for a niche you’ve never heard of might be the best SaaS ever!

If you are a marketer, know this: even in a crowded space, you can make a difference because there are a lot of companies out there that have a great product and are struggling with marketing. I often find a market need, like, “Private teachers need booking systems” and in my market research I find that the perfect booking system exists and nobody is using it. Lending your superpowers to a developer that’s charging ahead with building yet-another-whatever might be the best SaaS ever!

The Voyager 1 museum

Voyager 1 is, as of now, 22 billion kilometers away from home. One day, we’ll be a space-faring species and we’ll have Voyager 1 in a museum. It’ll be trivial for us to go and reach Voyager but we won’t retrieve it. What’s wonderful about Voyager 1 is not only the amazing science and engineering that we can see on the metal, plastics, cables, circuits, panels, batteries, etc. What’s amazing is it’s vector: direction and speed.

We’ll build a museum around Voyager 1 for people to visit and see it travel. The museum will have to be built very carefully, bringing materials from all directions at the same time, in a balanced way, to avoid affecting Voyager’s trip. Even the visitors will have to be controlled to avoid affecting it.

We’ll marvel at what once was the man-made object furthest away from earth, from home, from the cradle back when all of mankind lived there. Billions of minds will visit it and marvel through the millennia. The museum will act as beacon for commerce and science to stay away of its path… until that day.

One day, Voyager 1’s path will intersect with something else. It might be a planet, an asteroid, a star, a black hole. The odds are astronomical you might think, but so is, well, space and time.

That day unrecognizable humanity will gather to decide what to do. We’ll be mature enough to not need that piece of metal somewhere safe and instead we’ll say good bye.

Part of humanity will gather around, for weeks, maybe even months. We’ll have a festival in space about the 20th century, about how fucked it was, marveling at how close humanity came to self destruction and still produced Voyager 1. We’ll watch movies, attend concerts, both old and new. And eventually, the museum we’ll retreat and so we’ll we. We’ll all watch is silence as a relic of our infancy reaches the end of its life, as it collides and disintegrates. We’ll celebrate it, we’ll mourn it.

How can the price of oil or anything be negative?

The price of oil being negative is clearly a milestone of sorts, but $0 is not such a special price point. It feels special, it feels different, specially to consumers, but it’s not.

I won’t explain why oil got so low. There’s a mix of a pandemic going on, Russia fighting OPEC, etc. There’s also the difference or lack-of buying oil vs oil futures. I don’t know about that and I won’t speculate on that. Back to the problem of something costing $0 or even negative.

Let’s say you buy a trinket and it costs you $1 and then you can sell it for $2, making 100% profit. You go and spend $1000 in buying 1000 trinkets, you are about to make $1000 but then you realize 1000 trinkets take a lot of space, so, you rent a storage space for $200/month. Now you are making $800 per month. That’s ok, that’s a good profit.

One day, the prices of trinkets start to fall. First they fall to $0.9, and you sell them for $1.8, and now your profit is $700 because your revenue is $1800, the cost of buying 1000 trinkets is $900 and rent stays at $200. As the price continues to fall, this happens:

Buy priceSell priceRevenueCostRentProfit
$0.9$1.8$1800$900$200$700
$0.8$1.6$1600$800$200$600
$0.4$0.8$800$400$200$200
$0.2$0.4$400$200$200$0
$0.1$0.2$200$100$200$-100

See how it wasn’t required for the trinkets to get to $0 to make the business nonviable? At $0.2 per trinket, there’s no profit and you might as well put the on the sidewalk, and walk away. Except that you’d get fined, so now you are losing money.

This is the equation that all shops and warehouses run for all products.

For this example, I’ve assumed $200 was the rent for one month and that all trinkets would be sold in one month. Now, let’s imagine the trinkets don’t go down in price, but they sell more slowly. It takes 2 months, which means $400 in rent. If they stay in the warehouse long enough, the magic number at which it’ll cause loses would be much higher, even higher than $2.

This is why you often get business selling you products for less than the cost. Because they are losing money by keeping it around and they need to cut those loses. The magic number in which you go from profit to loses is never $0, it’s always above.

This means that at some point before oil hit $0 it already crossed the magic number at which holding oil was causing loses, it’s just that the loses were so high that it kept pushing down the number into the negative.

Finding a co-founder for your startup

Update: this list is now being maintained on it’s own page: Finding a co-founder

Someone recently contacted me at MicroMentor with a few questions about their startup, including, how to proceed without a business partner. I told him he should probably find one and then I discovered I had more co-founder search resources than I thought:

Any other resources you are aware of?

Converting a Python data into a ReStructured Text table

This probably exist but I couldn’t find it. I wanted to export a bunch of data from a Python/Django application into something a non-coder could understand. The data was not going to be a plain CSV, but a document, with various tables and explanations of what each table is. Because ReStructured Text seems to be the winning format in the Python world I decided to go with that.

Generating the text part was easy and straightforward. The question was how to export tables. I decided to represent tables as lists of dicts and thus, I ended up building this little module:

def dict_to_rst_table(data):
    field_names, column_widths = _get_fields(data)
    with StringIO() as output:
        output.write(_generate_header(field_names, column_widths))
        for row in data:
            output.write(_generate_row(row, field_names, column_widths))
        return output.getvalue()


def _generate_header(field_names, column_widths):
    with StringIO() as output:
        for field_name in field_names:
            output.write(f"+-{'-' * column_widths[field_name]}-")
        output.write("+\n")
        for field_name in field_names:
            output.write(
                f"| {field_name} {' ' * (column_widths[field_name] - len(field_name))}"
            )
        output.write("|\n")
        for field_name in field_names:
            output.write(f"+={'=' * column_widths[field_name]}=")
        output.write("+\n")
        return output.getvalue()


def _generate_row(row, field_names, column_widths):
    with StringIO() as output:
        for field_name in field_names:
            output.write(
                f"| {row[field_name]}{' ' * (column_widths[field_name] - len(str(row[field_name])))} "
            )
        output.write("|\n")
        for field_name in field_names:
            output.write(f"+-{'-' * column_widths[field_name]}-")
        output.write("+\n")
        return output.getvalue()


def _get_fields(data):
    field_names = []
    column_widths = defaultdict(lambda: 0)
    for row in data:
        for field_name in row:
            if field_name not in field_names:
                field_names.append(field_name)
            column_widths[field_name] = max(
                column_widths[field_name], len(field_name), len(str(row[field_name]))
            )
return field_names, column_widths

It’s straightforward and simple. It currently cannot deal very well with cases in which dicts have different set of columns.

Should this be turned into a reusable library?