technology Pearls

Facebook is a Ponzi Scheme

Now that you know what a Ponzi scheme is, I will tell you how and why Facebook is like a Ponzi Scheme. The argument is similar to how Paul Graham describes that Yahoo was a ponzi scheme in 1998.

Facebook posts huge revenues. In fact, recent reports are that Facebook is very profitable. This boosts both their respect in the world and their valuation. However, these returns, while real, are unsustainable. They exist and are sustained in the same manner that Ponzi schemes are. Facebook is a Ponzi Scheme.

Have you ever bought a Facebook ad? I have. I have talked to many, many people who have. We have spent hundreds, many have spent thousands or even more, experimenting with Facebook ads. They are worthless. Nobody ever looks at them, and nobody ever clicks on them. I just talked to someone who was trying to promote a book. He found it cost him over $100 in ads to sell one book. Moreover, as you increase your ad spending, people get used to the ads and just ignore them. So, your already low click-through rate plummets even further.

People go to Facebook to interact with their friends. It is fundamentally different from the ad platform that is Google. People go to Google to find something they need, possibly ready to buy, which a good percentage of the time can in fact be solved by someone's ad. Facebook ads, on the other hand, annoy users. They yield no real value, and thus no profits.

But, then, how is Facebook so profitable? Are they lying? No. They are growing. More and more people sign up to Facebook, and more and more businesses hear about how many people are on Facebook. It seems like a huge opportunity. TV shows and award-winning movies are made about Facebook.

Because of Facebook's presumed success, many small, medium, and large businesses individually and in turn experiment with Facebook ads. They spend hundreds or thousands or more on Facebook ads. At the end of the first run, they see bad ROIs. They tweak the ads and spend more money and try again. Nothing. So they stop, understanding that Facebook ads are worthless. Almost everyone I've talked to who has actually bought Facebook ads knows this. But, not everyone has bought Facebook ads yet. There are still more and more new businesses finding out about Facebook ads. As they grow, even more businesses give their money to experiment in destined failure.

Eventually, though, and this might take a long time, but it is finite, everyone will have tried Facebook ads and know that they are useless. Eventually, after 10 million businesses have invested $1000 each, and Facebook has earned $10 billion in revenue in total, then they will have run out of new customers and their revenue will dry up. A useless product is never sustainable. I wish I could short Facebook.

Now, it is possible that some extremely niche businesses have found limited utility from ads (for example, BustedTees and social games may be the lucky few). It has been shown, however, that some of the biggest advertisers are huge, outright scams and others have deceptive practices at best. Also, Mark Zuckerberg might have a fit of brilliance and then announce a revolutionary ad platform that somehow actually works on social networks. My guess is not. They haven't yet. What is clear from everyone I know who has advertised on Facebook is that it was a waste of money. Facebook promises big returns on ad spending, but delivers nothing. Yet, their value and growth continues because they can use that money to grow their user-base more and assert profitability (in this sense it's not quite entirely a ponzi scheme, but there is no closer idea). It's possible that they do not even realize that they are like a Ponzi scheme.

That's right, they may not even realize that their ad platform is completely useless because they always get new clients signing up and giving up their offering to the god of web 2.0 hype. They may be blind, as I used to be. They may be truly surprised when the supply of suckers runs dry.

More likely, in the end, they will get teenagers to pay a monthly fee to host all of their party photos. Of course, then the next VC-funded Facebook (just as MySpace killed Friendster, and Facebook killed MySpace, so will NextFB kill Facebook) will offer the same services and be free and take over the "market." The cycle repeats itself.

over 8 years ago on January 18 at 6:34 am by Joseph Perla in technology


Weby templates are easier, faster, and more flexible

Weby Templates are used in production on websites that have garnered over 500,000 uniques a month. They are incredibly easy to work with. They are the "templates for lazy people". They have 3 main benefits:

  • easier
  • faster
  • more flexible

The main codebase is implemented in 100 source lines of code. Extensive libraries only add just a couple hundred other lines of code. It is simple. It ensures that all tags have closing tags. It is compiled and fast. It is unicode compliant and safe. You can be fully fluent within 5 minutes since it is just Python and a transparent library.

Easier

Writing with weby templates takes less than 3 minutes to learn. Below is a sample:



import weby_templates as weby
from weby_templates.templates.lib import html

@weby.template()
def index(p):
    with p(html.html()):
        with p(html.head()):
            p(html.title('Hello, World!'))
        with p(html.body()):
            p(html.h1('Hello, World!'))
            p(html.p('Please choose from the following items:'))
            with p(html.ul()):
                p(html.li('Lorem'))
                p(html.li('Ipsum'))
                p(html.li('Dolor'))
            with p(html.div({'class':'footer'})):
                p(u'About | Links | ... | ')
                    


<html>
    <head>
        <title>Hello, World!</title>
    </head>
    <body>
        <h1>Hello, World!</h1>
        <p>
            Please choose from the following items:
        </p>
        <ul>
            <li>Lorem</li>
            <li>Ipsum</li>
            <li>Dolor</li>
        </ul>
        <div class="footer">
            About | Links | ... | 
        </div>
    </body>
</html>
                    

Fundamentally, weby templates are based on 3 simple concepts.

  1. First, use Python to its fullest. Weby does not invent a new esoteric language for you to learn. You know enough useful ones already. For mature developers, using the full power of Python makes things obvious and intuitive. Moreover, the simplicity allows the core code of Weby Templates to be just 100 lines. Every template is just a function that returns a unicode string. A template is just a string, not a fancy programming language or a complicated environmental-variable dependent rigamarole. Just call any functino that returns a string.
  2. Second, we include a decorator to make building the string easier. @weby.template() is a decorator that adds a first argument to the function. This first argument is an accumulator, conventionally named 'p', as in 'print'. With it, you can basically print out each html element in the text. It can also work with the 'with' statement in Python to ensure that all tags are properly enclosed.
  3. Finally, straightforward helper libraries exist that make writing html or xml or using filters (functions that accept and output strings) easier. For example, the html library is extremely useful. Every html tag has an analogous function in the html library, as seen above. The first argument are the content words, and the second argument is a dictionary of the attributes. If the tag is in a with statement, then the first argument is just the dictionary of the attributes of the tag (since the contents are contained within the with statement. That's all of the documentation you need to be productive in the library.

This README is generated with Weby templates if you want more examples.

./README.py > README.html

or

./README.py | ./weby_templates/tools/beautifier.py > README.html

Faster

Weby templates are written in Python, and they compile to bytecode. Moreover, they utilize the complete Python stack, so you can use the Python debugger tools, source code checkers, and your normal build process.

You get the whole python compiler and optimizer speeding up your templates.

More Flexible

Weby Templates are just Python functions which return strings. It makes no other assumptions. That means that you can write HTML templates with it using the minimal HTML lib, or you can write standards-compliant XML with the XML lib, or you can write your emails, or you can write your own helper functions to generate documents in a custom format!

Also, unlike in other languages, writing filters and nested subtemplates or modules of arbitrary nesting or which take an arbitrary number of inputs is simple: just write such a function that returns a string, and optionally use the Weby Template decorator if that makes your life easier. Usually it does. Every filter is just a Python function that accepts a string and returns a string. How do you interface with this? How do you truncate? How do you pretty print? The answer is obvious and in the standard lib.

Just accumulate an object, or even just create a unicode string generator. Any function that returns a string will be a template. For example, each of many_hellos_* methods below are all valid, equivalent weby templates:



            import weby_templates as weby

            def many_hellos(num=10):
                return ('Hello, World!\n' * num)

            def many_hellos_redux(num=10):
                def many_hellos_generator(num=10):
                    for i in range(num):
                        yield 'Hello, World!\n'

                return u''.join(many_hellos_generator(num))

            @weby.template()
            def many_hellos_decorated(p, num=10):
                for i in range(num):
                    p(u'Hello, World!')
                
            @weby.template()
            def many_hellos_decorated(p, num=10):
                for i in range(num):
                    # raw function does not append newline
                    p.raw(u'Hello, World!\n')

            @weby.template()
            def many_hellos_decorated(p, num=10):
                for i in range(num):
                    # raw function does not append newline
                    p.raw(u'Hello, World!\n')




            # a sub-template (equivalent to Django filter, Django sub-templates, and Ruby helpers)
            # Since they are just functions, they can be nested, of course
            @weby.template()
            def add_greeting(p, thing):
                p.raw(u'Hello, %s!' % thing)

            # the main template, which calls the sub-template
            @weby.template()
            def many_hellos_subtemplate(p, num=10):
                for i in range(num):
                    p(add_greeting('World'))
        

FAQ

Q: I get this unicode error: "Always work with Unicode", what is this?

A: Weby Templates are unicode compliant.

I love Python, but Python 2.x made some poor assumptions when it comes to unicode compliance which some other languages like Ruby do not suffer from. It contains both str and unicode types, and their interaction can sometimes cause problems.

Python 3.x fixes this by only having one string datatype: unicode. This greatly simplifies everything, but most code is still on the 2.x branch. To avoid annoying and difficult to debug situations, Weby Templates always outputs unicode, and it only accepts unicode. It does not try to intelligently or magically deal with improper strings; magic like that is hard to debug. The developer will explicitly deal with that before passing strings to Weby Templates. Explicit is better than implicit.

As a tip, make sure you always use unicode throughout your app, and only encode/decode strings when you are inputting/outputting data to the world. For reading from files, use the 'codecs' package in the standard library. Make sure that you read and understand this excellent article about Unicode from Joel Spolsky.

Q: I like that Django Templates constrain me. It helps me ensure that I keep templates strictly within an MVC framework. Can I do this with Weby Templates?

First, since it is possible and even easy for some calls in Django Templates to have side effects, be aware that Django only provides the illusion of constraints. In fact, database calls and state mutation can be embedded in the calls. The template language merely encourages an MVC style which template writers often adhere to.

Second, in practice, we have used Weby Templates and similarly are encouraged to write in an MVC style due to the nature of the tool. Mature app developers naturally have no compelling reason to violate the MVC framework, when it makes the most sense. Moreover, arguments are passed explicity to templates in the function arguments, unlike in Django where they are implicit and unpythonic, and the structure of with statements and accumulator naturally encourage an MVC style separation. Finally, since sometimes bits of html are generated in views (links, messages, etc), you can use the same template library and filters to generate these snippets as you use in your templates. Thus, you don't repeating yourself and have less, tighter code. You do not need a separate file to generate a separate 1-line template, just add a 1-line function to your Python scripts.

over 9 years ago on August 15 at 11:06 am by Joseph Perla in technology, open source


Apple's amazing customer support system

Pick up your phone. Dial 1-800-MY-APPLE, 1-800-692-7753.

Listen to the automated system tell you that he can understand complete sentences. Shake your head in disbelief. There's no way this is going to work.

Say 'I want to know if I can buy an AppleCare protection plan for my Mac Mini I bought 15 months ago.'

Notice how it asks you if you are asking for personal or business use. Stare in disbelief as it responds instantly and without a long awkward pause. Listen to the quick and conversational, not slow and stilted, tone of the computer voice. Let it automatically send you to the AppleCare department where you have no wait to begin speaking to someone.

Best phone system ever. I understand that it is very limited, but it fools you well enough to be a pleasant experience. It's a big stepping stone to even more powerful interactive phone systems.

over 10 years ago on August 23 at 5:50 pm by Joseph Perla in technology


Log Reader 3000

I wanted to show off another python script today.  I think it’s pretty cool.  It’s kind of like a very rudimentary version of something you might see in Iron Man.  And, of course, anything in Iron Man is cool.

I dub it Log Reader 3000.  It’s purpose?  It helps me monitor logs.  How?  Well, sometimes I need to follow a log in real time as it is written, but I can quickly get bored.  The log scrolls by endlessly while, very often, little new information spits itself out.  I can quickly lose focus, or at the very least, damage my vision after staring at a screen intently for extended periods.

Ideally, I want the log to simply flow through me, and if my subconscious notices something odd, then I can act on it.  If the log is read aloud to me, then I can work on other tasks and let my auditory memory and auditory processing take note of oddities on which I need to act.

So, I made a python script to read the log out to me as it is written. It is my first Python 2.6 script.  I take advantage of the new multiprocessing module built into the standard library.  I also use the open-source festival text-to-speech tool.

First, install festival.  sudo apt-get install festival in Ubuntu.  You probably want to set it up to work with ALSA or ESD sound.  By default, festival uses /dev/dsp, which means that you can’t use festival and any other program that uses audio (like Skype) at the same time.   Fortunately, and as usual, Ubuntu provides detailed, simple instructions to set up festival with ALSA: https://help.ubuntu.com/community/TextToSpeech .

Finally, just find an appropriate use case.  Note that most log monitoring applications would not be improved with Log Reader 3000.  If you just want to be notified of errors, you should have a program email you when an error appears in a log.  If you want to understand the log output of a program that has already run, understand that Log Reader 3000 is meant for live-running programs.  Yes, Log Reader 3000 can be modified to read any text file line-by-line.  But, you will find that reading ends up being much faster than listening to a slow automated voice, so I recommend that you just try to skim a completed program’s output with VIM.

So then why ever use Log Reader 3000?  It is useful for applications which fit all of the following criteria:

  1. you want to monitor a live running program
  2. and the debugging information is nuanced and you need a human to interpret it (i.e. it cannot be filtered programmatically) and/or you want to be able to intervene while the program is running to keep it doing what it ought to be doing in real time.

Applications:

  • Say that you are spidering the web, and what the spider should and should not be spidering is not yet well-defined, but a human knows, then the Log Reader 3000 can read aloud where the spider is, and the human can correct course as he or she notices the spider going astray.
  • Or, say that you are working on some kind of artificial intelligence.  Perhaps, the AI program can reason aloud and a human can correct or redirect the machine’s reasoning as it goes along.  I have no idea how or why an AI would do that.
  • Maybe you want to protect against bot attacks, but your aggressor is particularly clever and seems to avoid looking like a bot in all of the obvious ways.  You can pipe the output of your log into Log Reader 3000 and notice new kinds of suspicious patterns live while reclining in your chair or surfing the web.
  • You run a securities trading program.  You have numerous checks and double-checks to ensure that everything works correctly.  Nevertheless, you need to have a human monitoring the system as a whole continuously anyway, so you have Log Reader 3000 read aloud total portfolio value, or live trades, or trading efficiency, or fast-moving securities, or all of the above.
  • The lobby of your startup has a TV screen with graphs of user growth and interaction on the site.  You want to increase the coolness factor by having a computer voice read aloud some of the searches or conversations happening on your site live.
  • You make a living by selling cool techy art projects which blend absurdity with electronics.  You read aloud live google searches, or live wikipedia edits, or inane YouTube comments out of what looks like a spinning vinyl record.  Passersby whisper of your genius.

Once you have the application, just tail -f the log, parse out the parts you want the log reader to read (you can use awk for that, for example, or maybe a simple python script), and pipe that into the Log Reader 3000.

tail -f output.log | awk “{ print $1 }” | ./log_reader_3000.py

How does Log Reader 3000 work?  The main process reads in one line at a time.  As it reads in each line from stdin, it sends it to the processing queue.  The child process reads the last item in the queue (it discards the items at the top of the queue because those are old and we need to catch up with the latest output line) and then calls a function to say() the line.  The say() function simply uses the subprocess module to call festival in a separate process and then blocks until it is done saying it aloud.

Because having a computer voice read aloud a sentence takes a while, the log probably outputs many more lines than can be read aloud.  That is why a multiprocess queue is needed, and that is why Log Reader 3000 only reads out the most recent line which has been output, which is why it is most useful for specific applications.

Here is the script, log_reader_3000.py:

#!/usr/bin/env python2.6
import sys
import subprocess
from multiprocessing import Process, Queue
def say(line):
    say = '(SayText "%s")' % line
    echo = subprocess.Popen(['echo', say], stdout=subprocess.PIPE)
    subprocess.call(['festival'], stdin=echo.stdout)
def listen_to_lines(queue):
    line = 'I am Log Reader 3000.  The world is beautiful.'
    while True:
        while not queue.empty():
            line = queue.get()
        say(line)
queue = Queue()
p = Process(target=listen_to_lines, args=(queue,))
p.start()
while True:
    line = sys.stdin.readline()
    sys.stdout.write(line)
    queue.put(line)

over 10 years ago on November 21 at 4:33 am by Joseph Perla in art, hacks, technology


A Clean Python Shell Script

Guido van Rossum, the creator of Python, recently wrote a post on his blog about how Python makes great shell scripts, even (especially?) compared to shell scripts traditionally created in Bash and using purely shell commands.

Guido is absolutely correct.  Shell scripts birth themselves painfully from my fingertips.  Bash’s kludgy syntax irks my orderly sensibilities.  Typos frequent my unreadable scripts.  iffi?  Who invented this?

On the other hand, Python sticks to just a handful of language constructs, so the language does not force me to google how to create else statement every time I need to do it.  Python just makes sense.

One of the comments asks if Guido can show him some “really beautiful” python shell scripts.  I don’t mean to brag, and by no means do I think that my scripts in particular invite the light of the heavens to shine upon them, but I think I follow PEP 8 fairly closely and I pay attention to the brevity and clarity of my language.  I find that I can quickly debug my scripts because of their transparency at run-time and their concise self-annotating source code.

So, below I show a script which I call merge_branch.py. Do

chmod +x merge_branch.py 

so that you can run it from the command line with a simple ./merge_branch.py.  I alias it in ~/.bashrc.

alias mb='../path/to/merge_branch.py'

The script simplifies what I would have to do manually in git many times a day.  You see, git exemplifies a great version control system for keeping track of source code.  I create branches instantly.  This encourages me to work on bug fixes and new features separate from the main code base.  As others make changes, I can easily integrate their changes by merging the changed branch into my branch.  Git’s merge algorithm embarrasses any other I have ever used.  Some of Subversion’s merges still haunt my nightmares to this day.

So, git rocks.  Unfortunately, conflicts sometimes do occur.  So, the proper procedure for merging a branch needs to be followed carefully.  People are bad at doing things carefully. That’s okay.  We should spend more time on making mistakes being creative.  That is why we invented computers to do work for us.

For best results, merge the latest changes people made to master into your branch as often as possible.  Small changes incrementally will probably mean small merge fixes.  One big change will probably cause you major pains.  So, merge from master into your branch often.

If you use GitHub or another central repository with a number of other people, then you must do a number of things.  First, make sure that all the commits that you wanted to make are commited to your branch and that you didn’t leave any files out that you have not explicitly git-ignored.  This happens a lot in SVN and Git if you are not careful, and it is the greatest source of frustration for anyone who uses a system like this.  To human is to err, c’est la vie.  Then, git-checkout master.  Make sure that origin has the latest changes from your master.  Then git-pull the latest changes form origin (github) into master.  Then git-checkout your branch again.  Then git-merge master into your branch.  If there are any errors, fix them and commit, otherwise you are done.

Also, when you complete all the changes in your branch and all the tests pass, then you need to git-merge your branch into master and git-push it back up to origin (possibly github).  You need to follow the procedure above to ensure the latest master changes are included in your branch (preferably before you run the tests).  Then, you check out master, git-merge master into the branch (this will be clean since it should just be a fast forward because you already have the latest master).  git-push the changes to origin.  Finally, delete the branch that you just completed.

This tedium rotted my brain for weeks.  Finally, I resolved to write a script to solve the tedious parts, but bring possible errors to my attention if they occur.

Please let me describe to you a few features of the script.  First, I try to follow PEP 8 as much as I can.  I have read it at least times; you should too. Also, recite the Zen of Python every night before you go to bed.

Notice how I start the script with a shebang line which says /usr/bin/env python, the preferred way to start Python since it is most flexible.  For example, I can use Python 2.6, or my own local version of Python.

I use the logging module which is part of Python’s very large standard library.  Logging gives you so much for free.  For example, instead of commenting out print statements, just change the default logging level threshold.  Always use the logging module instead of using print for everything.  Always.  It’s as easy as import logging; logging.error(’…’).  Also, the logging.basicConfig(…) I use here is the same one I use everywhere.  Logging the time that a message appeared saves hours and hours when I debug long-running scripts.

Use the optparse module in every Python shell script you write (getopt is too weak and will end up being much, much less simple by the end of a non-trivial program).  Again, you get so much for free, like a -h help command.  The documentation for optparse explains how to do everything in detail.  Make sure you set the usage parameter.  Also, make sure you call parser.error() for input option errors instead of raising an exception yourself.

Write utility functions.  Use the power and simplicity of Python to your favor.  Here, I use call_command().  I use it throughout the script and it makes the code so clean and clear.

Finally, I like to put the main() function of scripts at the top.  That makes the most sense to me.  If I open a file, I want to instantly read what it does, not read what its utility functions do.  I put the utility functions below.  Of course, at the bottom, after everything else has loaded, I place the if __name__=”__main__” code and then call main().   This way, I can import this as a module (in, for example, py.test or iPython) to test the utility functions without running the actual script.  (warning:  Do not put anything except the call to main() at the bottom.  Otherwise, you may not realize under what circumstances you call main() and with what parameters.)

Here is the script:

#!/usr/bin/env python
import os
import re
import subprocess
import logging
import optparse

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s')

def main():
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage)
    parser.add_option("-m", "--merge-master", dest="merge_master",
                    action="store_true",
                    default=False,
                    help="Merges the latest master into the current branch")
    parser.add_option("-B", "--merge-branch", dest="merge_branch",
                    action="store_true",
                    default=False,
                    help="Merge the current branch into master; forces -m")
    options, args = parser.parse_args()

    if not options.merge_master and not options.merge_branch:
        parser.error('Must choose one-- try -m or -B')

    # Merging branch requires latest merged master
    if options.merge_branch:
        options.merge_master = True

    if options.merge_master:
        output,_ = call_command('git status')
        match = re.search('# On branch ([^\s]*)', output)
        branch = None
        if match is None:
            raise Exception('Could not get status')
        elif match.group(1) == 'master':
            raise Exception('You must be in the branch that you want to merge, not master')
        else:
            branch = match.group(1)
            logging.info('In branch %s' % branch)

        if output.endswith('nothing to commit (working directory clean)\n'):
            logging.info('Directory clean in branch: %s' % branch)
        else:
            raise Exception('Directory not clean, must commit:\n%s' % output)

        logging.info('Switching to master branch')
        output,_ = call_command('git checkout master')

        output,_ = call_command('git pull')
        logging.info('Pulled latest changes from origin into master')
        logging.info('Ensuring master has the latest changes')
        output,_ = call_command('git pull')
        if 'up-to-date' not in output:
            raise Exception('Local copy was not up to date:\n%s' % output)
        else:
            logging.info('Local copy up to date')

        logging.info('Switching back to branch: %s' % branch)
        output,_ = call_command('git checkout %s' % branch)

        output,_ = call_command('git merge master')
        logging.info('Merged latest master changes into branch: %s' % branch)
        logging.info('Ensuring latest master changes in branch: %s' % branch)
        output,_ = call_command('git merge master')
        if 'up-to-date' not in output:
            raise Exception('Branch %s not up to date:\n%s' % (branch, output))
        else:
            logging.info('Branch %s up to date' % branch)

        logging.info('Successfully merged master into branch %s' % branch)

    if options.merge_branch:
        logging.info('Switching to master branch')
        output,_ = call_command('git checkout master')

        output,_ = call_command('git merge %s' % branch)
        logging.info('Merged into master latest branch changes: %s' % branch)

        output,_ = call_command('git branch -d %s' % branch)
        logging.info('Deleted safely branch: %s' % branch)

        call_command('git push')
        logging.info('Pushed master up to origin')
        logging.info('Ensuring that origin has latest master')
        stdout,stderr = call_command('git push')
        if stderr == 'Everything up-to-date\n':
            logging.info('Remote repository up to date: %s' % branch)
        else:
            raise Exception('Remote repository not up to date:\n%s' % output)

        logging.info('Successfully merged branch %s into master and pushed to origin' % branch )
def call_command(command):
    process = subprocess.Popen(command.split(' '),
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    return process.communicate()
if __name__ == "__main__":
    main()

over 10 years ago on November 17 at 11:00 pm by Joseph Perla in hacks, technology


Ruby is a Ghetto

Zed Shaw (the creator of Mongrel) not long ago described how the Ruby on Rails community is a ghetto.

Working with both Python and Ruby now, I learn every day how much Ruby, too, is a ghetto.  Nobody documents anything (e.g. solr-ruby).  The few projects that have documentation do not document clearly or completely (Rails…).

Moreover, take Ruby’s standard package management system: rubygems.  A simple gem install uses up 600+ MB of RAM on my SliceHost machine.  But again, lack of documentation scars this project even more deeply.  Take a look at its Frequenty Asked Questions Page:

[I lost my internet connection while composing the answer to this question. Unfortunately, I now no longer recall what I intended to write. The answer (or at least part of the answer) is buried deep in the RubyGems mail archive. I’ll try to update this after a bit of research. Sorry for leaving this question hanging.]

Seriously, “I now no longer recall what I intended to write.”  For how many months has this lunacy been online?  Ruby is a ghetto.

over 10 years ago on October 26 at 11:14 pm by Joseph Perla in technology


Switch windows quickly

I want to avoid RSI.  So, I want to use the mouse as little as I possibly can while I focus on my keyboard.  Unfortunately, alt+tabbing between windows takes far too long and annoys me with how much I have to think to move between the windows.

I often have several windows open.  I want to switch between them very quickly.  So, I created a program using Python and Xlib that generates another program.

The program moves my mouse automatically to a specific location and then clicks automatically.

The program also creates a keyboard shortcut in Gnome.  So, I can type something like Alt+q which calls the program above, moves the mouse to a specific location, and then clicks.

Finally, the program also generates a program which, when called, opens up many windows at specific locations in a grid:

If I combine all of these programs, I can focus between windows very easily. I can move the mouse between 12 spots in a 4×3 grid using the keyboard letters Q,W,E,R, etc like below:

To move to the Top-Left window, I hit Alt+q, which moves my mouse to the top left and clicks, focusing the top-left window.  To move to the bottom right, I press Alt+v, then the mouse moves to the bottom right and clicks, focusing into the bottom right window.

Basically, when I’m programming, I never have to move to my mouse to switch between windows very quickly.  I can open up 12 files at once, and switch between them swiftly and deftly.  When you work on a large application, this becomes very useful.

I’ve open-sourced the program here: http://github.com/jperla/mouse-focus-shortcuts/tree/master .

over 10 years ago on September 18 at 8:19 am by Joseph Perla in hacks, technology


Kohl looking into telecom pricing practices

I have been saying for a long time that AT&T, Verizon, and others operate in a market with little real competition which allows them to charge ridiculous rates and have unfathomably bad customer service.  Finally, one senator begins investigations into their text messaging pricing practices which have increased by laughable amounts: http://mobile.slashdot.org/article.pl?sid=08/09/10/1845210

over 11 years ago on September 15 at 2:43 am by Joseph Perla in technology


Y Combinator Application Guide

Y Combinator, a kind of mini-venture capital firm, invests tens of thousands of dollars ($$$) into very early seed stage start-up companies run by smart technology hackers.  They wanted to fund me in Summer 2008.

I applied to Y Combinator two times.  The first time, when I applied with my friend Mason for the Summer 2007 round,  I arrogantly presumed that Paul would lavish on us praise and beg us to fly to California to work with him.  I spent no more than an hour on the application.  We had no passion in the idea we presented.  Our projects list hinted at nothing particularly remarkable or unique.  Our analysis of the idea and our competitors delved only into the shallowest parts of a deep lagoon.

The second time, when I applied alone in Summer 2008, in an inspired moment I sat down in Starbucks for a solid few hours to work on the application.  I strived for excellence, not perfection.  A few months prior, I had briefly glimpsed the semi-successful application of Liz Jobson and Danielle Fong.  I recalled their deep detail and thoughtful writing, so I imitated that kind of deep analysis which shows off one’s mastery of logic and breadth of experience.

I wish I had known how to write a good application the first time.  So, taking my cue from Brian Lash’s recent question on Hacker News, I helped him out.  I write here a slightly expanded version to help out anybody else who wants Paul Graham & co. to fund his or her startup.

If I were to advise myself in 2007, I would recommend that I write briefly but write a lot.  This advice seems contradictory, but I mean it in a very specific way.  My first application, I kept brief.  I did not want to swamp YC with a tome of text. I saved many of my accomplishments for the interview.  Do not do this.  Write, write, and write some more.  Write everything interesting and unique about yourself.  If you have doubts about a statement you made about a competitor, qualify it.  Don’t vacillate, but at the same time don’t seem shallow, ignorant, and inexperienced.

Of course, once you’ve written all that, you have a very long application.  Now, take out filler words.  Compress ideas that take up two sentences when you can use just one. If you waste two words in a sentence, delete the whole sentence and write it again from scratch.  If you see a phrase that you think an investment banker might use on his resume, nuke it.  Achieve a high density.  In my experience, the YC crew truly pores over these applications to understand all of the meat of it.  They do not skim your application when it has rich content.  Cut, cut, and cut some more.

Now, step back and look at your application.  If you have very little writing left, real content, then you may not be the best fit for Y Combinator this year.  That’s okay.  It’s good you know now.  Take this year off and work on some interesting, hard projects that nobody has done before.  Bounce your idea off of the smartest person you know.  Hell, micro-test the idea.  Then, repeat this process.

Step back, look at your tight list of accomplishments.  If it’s long, that’s great, since reading something long but rich in content everyone loves to do.  The length indicates strength.  In my limited experience, I think this is how I made my application successful.

Here’s some of my application below (I elided some less relevant parts). I was accepted for Summer ‘08 2008 but decided to pass this time for a variety of reasons.

—————————————-

What is your company going to make?

I’m open to anything. Here’s one idea:

————–

Have you ever scanned a document before? How was that experience?

It was terrible for me, too. Everyone I have ever asked has agreed that it is physically painful. But, there is a solution, one based on understanding actual human needs. What is wrong with the scanners of today?:

* slow (takes time to heat up)

* slow (scanning at a high dpi takes a long time)

* complicated (please select the dpi, now select bla, now bal[sic]…)

* cumbersome (files generated at high dpi are huge, slow down system)

* cumbersome (OCR’ing a document is a whole other rigamarole)

What do people really need?  Simply a decent, readable scan of the document. This should be as easy as holding the paper up to face the monitor.

Imagine that.

I propose that I sell a device which is basically just a decent-resolution CCD chip with a special lens which connects to a computer (wired at first, but v2 wireless). Scanning a document is as simple as holding the camera up to a document and clicking. In my tests, scanning a whole text books takes 5-10 minutes. This is a game-changer. I’ve worked with an ip lawyer to file the provisional patent on this and a few other aspects of the designs.

[BY THE WAY, IF ONE OF YOU WANTS TO HELP ME BUILD THIS, I'M ALL EARS. I'M AN AI HACKER NOT A HARDWARE HACKER. OH, BY THE WAY, I USED A DIFFERENT IDEA IN THE INTERVIEW ROUND, NOT THIS ONE SINCE I'M SKEPTICAL OF THE MARKET FOR THIS PRODUCT AT THIS POINT. NEVERTHELESS, IT'S VERY COOL. I WANT TO BUILD THIS FOR MYSELF!]

For each founder, please list: name, age, YC username, email address, personal url (if any), and current employer and title or school and major. List the main contact first. Separate founders with blank lines. Put an asterisk before the name of anyone not able to move to Boston June through August.

….. [Be sure to put your blog here. Don't have a blog? Make one. Blog about whatever is on your mind. Blog about your hacking.

To be honest, an Ivy League pedigree probably helped.  Also, my computer science degree (as opposed to Economics or Business one) probably encouraged YC's faith in me.]

Please tell us in one or two sentences about something impressive that each founder has built or achieved.

Looking at some things in ~/projects folder: ……..

[Here I mention a few of my projects, with links to open source code, web pages, anything I can publicly show. I didn't spend more than one or two sentences describing any one project, but I listed many of my most interesting projects and why I worked on them. YC likes to see you working on real problems, so I talked about problems I solved for myself and for others directly

They want to see that you think creatively and that you actually finish things.

It goes without saying that you should list projects which uniquely describe you.  Building a toy language in Programming Languages class many people probably do.  Yes, it may have taken you a long time, and you may have learned a lot, but you do not necessarily stand out.  Writing a CAPTCHA solver to hack Digg few people do or can do.]

Please tell us about the time you, ljlolel, most successfully hacked some (non-computer) system to your advantage.

…… [I talked about my shotgun email to dozens of startups here in Silicon Valley which gave me the opportunity to meet a lot of cool entrepreneurs.  I'll probably blog about this at some point in the future.]

Please tell us about an interesting project, preferably outside of class or work, that two or more of you created together. Include urls if possible.

(see above) [I applied alone, so group projects inapplicable.]

How long have the founders known one another and how did you meet? Have any of the founders not met in person?

n/a [Again, I was a sole founder.]

What’s new about what you’re doing? What are people forced to do now because what you plan to make doesn’t exist yet?

(see above) Basically, nobody ever scans anything because it takes forever, doesn’t really do what you want (you just want a readable, small image and for the document to be searchable),

What do you understand about your business that other companies in it just don’t get?

Scanner manufacturers try to pack in the highest dpi they possibly can. They focus on resolution, when they should be focusing on the user experience. Speed is what they should optimize, but I see no scanner manufacturer doing that.

Who are your competitors, and who might become competitors? Who do you fear most?

HP, Xerox, etc, also ScanR, Qipit, Evernote …… [I go on to be brutally honest about the difficulty and vulnerability of my position as a hardware startup in a crowded field. Remember, you are writing for some very, very smart people. They want to see your analytical thinking skills here. They want to see you be realistic, not delusional.]

……. more questions, answer analytically deeply, answer honestly to the best of your ability ……

If you had any other ideas you considered applying with, feel free to list them. One may be something we’ve been waiting for.

…….. [I always think of new ideas and discuss them with friends. I chose 4 and listed them here. I crisply described each in no more than 2 brief sentences.]

over 11 years ago on July 20 at 10:37 pm by Joseph Perla in entrepreneurship, hacks, life, money, personal, technology, ycombinator


TipJoy ingeniously simplifies and enables micro-payments

I have already written about my distaste for advertising.

Sometimes, however, these newspapers and blogs manage to make something of value. A particularly hard-hitting expose in a newspaper, or a particularly helpful guide in a blog, offers to people real value. Nobody can create this kind of content every day, or probably even every week or month. A subscription subsidizing the page-filling fodder misallocates wealth. I want to pay for the value more directly, not indirectly through ads.

Or, more commonly, a blog post provides for me a little bit of value. Not $50/month in value, but perhaps 50 cents/month. No payment system can pass around payments like that easily for the user, especially without ridiculously large credit card fees. How can I tell Michael Arrington of TechCrunch that his site I find useful sometimes? Combined with the millions of others who find his site just a little bit useful, he can make some money directly from us.

TipJoy.com

Fortunately, I think TipJoy solves many of these problems. The site only launched a few months ago, so it has to grow quite a bit before it reaches a tipping point. Nevertheless, TipJoy designed their product beautifully.

Registration

First, for new users, creating an account takes a few seconds. Just click on the TipJoy button, write in your email, quickly create a password, and finish up. No credit cards required. A new user signs up by tipping, integrating the first-use with registration. Ingenious.

They follow a model that bar’s use where you can set up a tab and pay later. Readers get drunk tipping blogs here and there all around, running up a huge tab. Users only pay once the tab gets large enough to justify the credit card fees. Their model differs from the bar, of course: just like the tips themselves, paying the tab is optional. Nevertheless, through this system, TipJoy encourages people to sign up easily, and only over time pay off their tabs.

Tipping

Second, once signed in, tipping takes just one click. No annoying confirmation steps required. No complicated questions about how much you tip. You get one choice: 10 cents. Of course, you can tip more if you want. This encourages readers to tip generously around many sites across the web. The tab builds up invisibly behind the scenes.

When I first read about TipJoy, I knew that it, or a model very similar to it, would take over the web. I think it will take time. However, I will support this service by putting it up on my blog. Maybe, one day, I will write a post that many people find just a little bit useful, and I can finally monetize this blog :) .

over 11 years ago on April 20 at 5:45 pm by Joseph Perla in entrepreneurship, money, technology


Make your own chimes

I just bought a Mac Mini.  I love it.  Apple spent a lot of time polishing OS X.

I configured OS X to create a chime, a 21-st century chime.  In the Date & Time settings, I selected it to tell me the time every hour.  At 6pm, a voice from the computer says, “It’s 6 o’clock.”  At 10pm when South Park comes on, it reminds me by saying “It’s 10 o’clock.”  The chime keeps me conscious of the time passing when I’m online.

I dual-boot Ubuntu on this Mac Mini.  Ubuntu, unfortunately, does not have this chiming feature. However, I set it up in minutes.  I installed festival, the free open-source text-to-speech synthesizer, as well as an American voice (I struggled to understand the British voice).

sudo apt-get install festival festvox-kallpc16k

Then, in crontab, I added one line:

0 * * * * echo "(SayText \\"Its`date +\%l` oclock\\")" | festival

Now I easily keep track of the time.

over 11 years ago on April 17 at 1:12 am by Joseph Perla in hacks, technology


How to check email two times a day

Tim Ferriss popularized the idea that you should limit the amount of time you spend checking email every day.  He espouses a philosophy of life called the low-information diet.  By following these guidelines, you get more done and, more importantly, feel less stressed.

One of his suggestions about email spread across the blogosphere very quickly because of its simplicity and practicality.  He recommends that you check email only twice a day (or preferably less often) and strictly adhere to that rule.  I started following these guidelines a few days ago, but I easily relapse.  Nevertheless, I do a few things to try to stay on the wagon:

  • Add a message to all outgoing emails:
EXPERIMENT: I will be checking email 2 times a day at 1pm and 6pm pacific time.
If you need me earlier, then please contact me below.

And of course I put my contact information below.  With this signature, I do not worry about missing out on important and urgent information or replies.

  • Delete all links, shortcuts, and bookmarks to GMail
  • Set up a script to automatically open up GMail at 1pm and 6pm every day.  In Ubuntu, I write just one line in crontab:
0 13,18 * * *  export DISPLAY=:0 && firefox https://mail.google.com/

Linux makes hard things easy.

over 11 years ago on April 15 at 9:02 pm by Joseph Perla in hacks, life, technology


Untenable advertising

Ads annoy me. Ads annoy everyone.

ads suck

More importantly, I cannot envision building a serious business which depends on these kinds of banner ads, or even text-link ads. But many websites do: TechCrunch, Project Wedding, Reddit, MightyQuiz, Justin.TV, Scribd, Loopt, and so on.

I use many websites online every week. Many provide a lot of value to me, they help me do my job more quickly or help me live my life more easily. They offer me so much value, I would even pay to use them. Some of these services include Google, Amazon, GitHub, Bank of America, Kayak, Craigslist, and a number of blogs like Slashdot, XKCD, and so on.

Except for Google services and the content creators (the blogs), I pay directly for the value these websites give me, given how little the services cost. The smartest ones all do. Kayak, for example, makes money off of the airline and hotel referral fees, not ads.

Unfortunately, many startups think that Google’s AdWords will be their sole source of revenue for their website.

Weakness

But, advertisements as a business model suffer from a fundamental weakness: advertisements indirectly monetize a website. Sometimes, the indirect monetization means that you make more money. Often, it means you make less.

Imagine you make a technically amazing product on your site. Nothing else exists like it. I sign up, and I notice that you provide an incredible amount of value to me. I would be willing to pay a large monthly fee for that. Instead, though, you monetize the website through advertisements. I never click on the ads. I visit to your site to use your product, not to buy chainsaws. You make no money off of my use. Even if I clicked on a few ads, you still make less money than if I paid a monthly fee.

That doesn’t work. You get none of the pay off from the value you provide me, while simultaneously actively annoying me. Project Wedding offers a good example. I would use Project Wedding to review, for example, photographers and find the perfect photographer for my wedding. The Google Ads at the top for random photographers who happen to pay for the ads do not help me; they only confuse me. A referral fee model makes more sense, or preferably an even more creative never-before-seen model.

The Quintessential Advertising model

Now, for a few kinds of websites, advertising makes sense. When I’m looking for a chainsaw to buy, I google “chainsaw” to find results, but I also see “chainsaw” sponsored ads on the right. That makes perfect sense; it directly provides me value. Google search exemplifies a great use of advertising. As Google’s customer, you watch Google continually provide us a better, more targeted service. It is the Yellow Pages model.

The Common Advertising Model

Advertising does not make sense in most other models. It does not provide value any other way, but it does annoy me. Nevertheless, I would use advertising if I started a company that built one class of product: products that provide little to no value. Examples include most television programs (think “reality” TV) and online games. People spend time watching random television shows to waste time and not think. These provide little (negative?) value. You have absolutely no opportunity to get people to spend money on your silly time-wasting flash game. You can, however, put ads along the side. With this kind of advertising, you transform your customers from the people playing the games into the advertisers. The advertisers pay you to reach out to these game-players, at the expense of the already-void user experience. Advertisers pay more.

Most magazines, newspapers, and blogs also fall into this category. Reddit and Digg as well. Little to no (to negative) real value offered. Newspapers and magazines offer nominal subscription fees, not because they cover the cost of the reporting–it probably does not even cover the cost of the ink– but because advertisers demand that they know real circulation numbers. Advertisers assume that if you pay at least a little, you probably read it and look at the ads. Because content creators cannot extract enough money from you directly into paying for all of the reporting and server overhead, they resort to becoming the servants of advertisers. Digg now displays (or at least recently used to) very annoying flashing ads.

Make something useful

But if I want to create something that depends on this kind of advertising, why do I want to create it in the first place? Why do I want to make something that nobody values, that nobody finds useful? For me, I cannot do that. I want to make something great, not a time-waster.

over 11 years ago on April 8 at 8:10 pm by Joseph Perla in entrepreneurship, technology


Stanford’s Entrepreneurial Thought Leaders Series (now on your iPhone!)

The Stanford Technology Ventures Program runs a well-developed incubator for tech businesses at Stanford University.

STVP offers some very cool resources free to the world. For example, I have been listening to their Entrepreneurial Thought Leaders audio podcast. The program brings in some of the greatest entrepreneurial forces in Silicon Valley today. Some of the speakers include

and so on.

While I was listening to Mike Maples and Ron Conway give their talks about angel investing, I had trouble following along and knowing who was saying what. I had subscribed to the talks through iTunes. When I visited the site, I noticed that they published videos as well, but in a Flash format, and not available as a podcast. I followed the videos much more easily.

So, in accordance with their fair use license, I decided to scrape the video metadata, download the video files, transcode them into an iPod-acceptable format, and republish them in a simple video podcast format. I now have scores of these talks in my iPhone and on my Mac Mini to view at my leisure.

No