Mar 20th, 2010 | Atlanta, Marketing, Opinion, Personal, Social Media
I’ve been organzing meetups in the Atlanta area since January 2007. Over that time I’ve organized over 50 meetup events, they’ve typically achieved average ratings of 4.5 of 5 or better, they’ve typically had 50 or more people attend, I’ve helped at least five (5) other people launch their meetup groups, and the member list for my original meetup group has grown to having more members than all but one other business-focused meetup group in the Atlanta area. During that time I’ve learned a bit about what it takes to be a good meetup organizer.
Recently someone asked me yet again for advice on how to grow their meetup so I decided this time to blog about it. Let me give the caveat that this is what has worked for me and for my type of meetup but it might not be perfect for yours. My groups have been focused on web/startup/marketing/tech and so I don’t know what works best for a mom’s meetup, for a hiking group or a singles club. Still, people are people and I’m sure anyone organizing a meetup can find something of value here. Here they are, in no particular order (some I fail to do consistently though I know I should; sometimes life just gets in the way):
- New organizers always try "to get input from everyone." From experience I’ve found that to be a waste of time. Find two (2) other people and form a planning team. Map out 5-6 topics, possibly starting with a "101" meetup and build from there.
- Meet quarterly with your planning team to plan so you always have 3 events on the calendar, more if possible.
- Do listen to feedback, but don’t wait for feedback before moving forward. Most people just want to attend meetings, few actually are willing to contribute a significant effort on a consistent basis even if they say or think they will. If people promise to contribute expect they will not follow through until they have proven otherwise.
- As much as possible be the catalyst and facilitator, not the featured speaker at every meeting (people will get tired of you if you do.)
- Schedule 3 to 6 presenters for a monthly meetup (more than 6 works if it’s a workshop and they are there to provide expertise.) It gives multiple perspectives and it keeps you from having failed meetings from building anticipation for a meeting, having lots of people show up and then only to learn that your featured presenter’s "kid got sick" so they decided to cancel.
- Do your best to get people from outside the people who usually attend your meetings to present. There’s the old saw "Familiarity breeds Contempt" (i.e. "I don’t need to attend to hear them talk; I know them already and can talk to them whenever I want.") Bringing in outsiders also makes people aware of your group that might not normally seek it out or attend. If they have an influence base such as on Twitter they will promote your group because it promotes them.
- Only ever schedule a person to present to the group once per year. If you frequently schedule the same people to present your members will think "I’ve already seen them, I don’t need to see them again." That means be sure to get them to talk on the topic where they are the best sui have the most bang for the buck since a lot of people will jump at any chance to present and you really want to get them where they will shine.
- Post a meetup page for each meetup event that includes links about the people who will be presenting including their Twitter account and a short bio. I like to link to their LinkedIn page for consistency, and also link to their company. Be sure to include an evening agenda so people can see when it starts and when it ends. Here’s an example meetup page that has all these things.
- Set up a Twitter account and a Facebook fan page. Always tweet and post about your events in advance and to thank your presenters/participants afterwards.
- Set up a Twitter hashtag for your meetup group (i.e. @StartupAtlanta and #OnStage.) Give people a handout at each meetup with the account, the hashtag and all the presenter’s/participants Twitter accounts and ask your members to tweet about the event.
- Send out emails in advance of your meetups that are hand formatted to look different from the one’s send out automatically by meetup as people tend not to read those. Here’s an example notification email.
- Send an email out about the most recently meeting and reminding them about the next meeting and thank the people who participated/presented.
- For my groups I have focus mostly on featuring local people for our regular meetings but when nationally known people are presented I make them special events. Some organizers always try to get one national calibre "rock star" for their events, and that works for them. Pick what works for your group.
- Keep vendor influence to a minimum; keep it about the people attending.
- Run a meetup only if you really want to help people and/or build a solid community and not if you’ve just got the idea "Hey I can sell my services to this group." The latter can be a serendipitous result but it’s painfully clear to practically everyone who might attend that if your motivations are to sell them (almost) nobody will want to attend.
- Pay it forward, focus on what’s good for the group and the community you envision building, not what’s you are hoping to get out of organizing
- Shake up the format. Have presentations, panel discussions, roundtables, workshops, etc. The topic should make the format obvious. For workshops, recruit lots of helpers. Don’t over worry about format, try a bunch of them, communication will happen ad-hoc (suggest Twitter or make a Google group), and let the topics you pick determine the level of competency. The more detailed your topic announcement the more likely you’ll get the right people.
- Don’t be afraid to ask anybody to present. I’ve never once been turned down except for people simply not being available at the given time.
- Look for ways to hold joint meetups with other groups that have cross-over. (Beg meetup on their forums to more easily enable shared meetups.) If possible take the lead in these joint meetups and get people to RSVP at your meetup group’s page (if possible, and at least until meetup enables shared events.) If you do these frequently you’ll all get lots of benefit and you’ll grow your group.
- Charge for meetings, $5 to $10, starting with your 3rd meeting (assuming you are gaining momentum.) If you don’t charge more than half of your RSVPs will be no-shows. If you charge, only between 10-30% will be no-shows.
- Be aware that many of the people who attend your meeting early on will start attending only sporatically as their lives evolve. That’s normal and don’t take it personally.
- Don’t try to do too many different groups. Unless you are able to make a living from organizing meetups, which is a potential but a really hard way to make a living, it’s really hard to do more than one well, two at the max. I’ve made that mistake and I’ve recently pared back to two with a potential to phase out of one of them in the near future assuming I can find the right people to take over.
- Find a good place to have meetings, not a restaurant unless its set up for meetings in a special room. This is the hardest part. Look for a local coworking space like Ignition Alley. A college or university may also be very open to hosting community meetings as Georgia Tech has been for some of my meetups.
- As for location, you’ll need to decide what works here. In Atlanta you’ll find a bulk of in-town people and a bulk of "up 400" people, and then everyone else is scattered. Pick one and let someone else do the other (you can’t please everyone, so don’t try.)
- Finally, set a consistent date, time, and location. Always have that if there and then so people can get used to it, and if at all possible, never cancel a planned meetup or many people will loose faith in your ability and stop RSVPing for your events.
Well that’s about it for today. I’m sure I missed a few of my own "best practices" and I’m sure there are a ton of other’s I have yet to uncovered but these should get you started.
If anybody has other suggestions please give your best practices in the comments. Be sure to mention your group(s) and how long you’ve been organizing, and include links to their pages on Meetup.com.
Feb 28th, 2010 | Contrarianism, Opinion, Startups

Yesterday Vivek Wadhwa who has recently become one of my favorite authors on startup-related topics wrote a somewhat inflammatory post on TechCrunch entitled "Can Entrepreneurs Be Made?" In it he asserts that entrepeneurs are made, not born, and it’s somewhat inflammatory because he calls out Jason Calcanis, Fred Wilson, and other Silicon Valley VCs as being wrong in their previously stated beliefs that what drives someone to be a great entrepreneur is innate, and thus that they are born.
In reaction Mark Suster, a entrepreneur-turned-VC and another of my favorite authors on startup-topics, responded with "Entrepreneurship: Nature vs. Nurture? A Religious Debate." Mark takes issue with Vivek’s thesis citing his experience and intuition as a recent father and calls out Vivek’s use of stats by implying his was based on a "faulty model" although he does state up front his point-of-view is "purely subjective." Mark goes on to presume Vivek may have "used hyperbole to get more readers" (which might be true though I’d expect that the TechCruch editors are more likely to be the culprit there…) and then complains about Vivek "attempting to “prove" unprovable facts (based on) this kind of data manipulation."
I have incredible respect for Mark but I can’t help but sense a tiny bit of defensiveness in his post. As a VC Mark makes decisions every day that will have profound effect on the lives of entrepreneurs and their families and fortunes. But it’s not uncommon that a subconscious defensive reaction is triggered when evidence comes to light that indicates a person’s important decisions might have been made on faulty criteria (see: choice-supportive bias, post-purchase rationalization and escalation of commitment.) I’m not saying Mark is wrong (or that he is right) but it felt like he was being defensive (as I have been recently.) Even so, if Mark was being defensive I’ll willingly give him a pass because it’s hard to overcome that which makes one human.
Back to the debate at hand; I sit on the fence. While I don’t know which perspective is correct I think the focus on this debate is actually harmful.
I assume that Mark shares Vivek goals and the goals of many others which are "…to boost economic growth by increasing the number of successful high-growth startups."If true then escalation of this nature/nurture debate is taking the eye off the ball.
If Mark and those who strongly believe in the nature convince policy decision makers they are correct then chances are those policy decision makers won’t explore subtlety. If there’s nothing we can do to cultivate entrepreneurs since they’ll bubble up to be recognized on their own, why do anything? Game over.
Wouldn’t it be better to look past the debate and instead focus on cultivating and educating entrepreneurs regardless of if they are made or born? Google has spawned a huge number of startups; were they all born? Maybe they were; Goggle has had many other employees who have not gone on to launch startups. But the fact that Google has spawned so many and most other companies in other regions haven’t proves (at least to me) that a strong catalyst results in more latent entrepreneurs taking action and launching innovative companies. When such a catalyst doesn’t exist those latent innovative entrepreneurs continue to do what their experience and environment present to them as options: be an intrapraneur climbing the corporate ladder or launch a replicative business.
In Atlanta where I’ve lived most my life, the holy grail for many is to work for one of the eleven (11) Fortune 500 companies that litter our landscape. As a graduate of Georgia Tech, one of the better technology schools in the nation and one that houses a very active state-funded accellerator, the majority of students aspire to work for one of those big companies because that’s the local culture. That’s what students talk about and that’s what the administration talks about. Going to work for a big company is what is expected of successful Georgia Tech graduates.
Most people aspire to the level of their peers. If their peers are not launching innovative startups the majority don’t even think to launch innovative startups. To say the vast majority of students who attend Stanford where many of those ex-Googlers who are now launching startups attended are genetically predesposed to be entrepreneurial whereas the vast majority of students who attend Georgia Tech (a top 10 ranked educational institution itself) are not strikes me as a bit too much confirmation bias.
As an aside: I think having so many Fortune 500 companies in Atlanta might be much more of a curse than a blessing. We have over 50 interactive agenies locally and they mostly suckle on the teets of these companies rather than aspire to create the next Google and drive real economic growth in the region instead. The people running these interactive agencies are entrepreneurs but the local culture and entrepreneurial patterns they are familiar with has had then focus on replication and not innovation. And sadly our local Fortune 500 companies do almost nothing I am aware of to foster startup innovation in our region.
Consider Shaquille O’Neal, one of the most dominant players in the history of the NBA. Would O’Neal have ever been drafted by the Orlando Magic if he had never met Dale Brown in Europe who was LSU’s men’s basketball coach at that time? Consider Sergey Brin and Larry Page. Would they have acheived Google’s level success had Sergey’s parents never left Moscow or if Terry Winograd had discouraged Page from analysing the link structure of the web? Clearly people who launch successful innovative startups are influenced by their life paths, their peers, their mentors, the options they are presented by society and a huge amount of luck, no?
Put another way, what’s the likelyhood that situated in remote villages in Africa or in the Amazon there are not 100s of would-be Shaqs who, without opportunity, will never be discovered? How many young car enthusiasts might end up being a leading NASCAR driver if they only had the ability to try? What if every University throughout the country had the startup culture and experienced startup advisors found at Stanford? What if government money across the nation spent on economic development was less focused on the zero-sum game of getting a large employer to relocate from another region and instead were focused on encouraging and supporting entrepreneurs to launch innovative startups?
I’m a rarity from Georgia Tech; I started my first real business about a year and a half before I graduated. But that was after working as a co-op student for many quarters at both Owens-Corning Fiberglass and later at IBM. When I started IBM I was completely enamored with them. After two work quarters though I left IBM thoroughly disgusted and started co-oping with a small consulting firm. It was there after watching my employer fumble I came to realize I could easily join forces with a co-worker and we could run our own business. Since then I’ve run many businesses, a few of which have done well and one that grew very rapidly over five years. During that entire time I can honestly say I had few if any real mentors and thus made more mistakes than any one person should be allowed to make! Had I had a better experience at IBM or had the owners of the consulting firm I worked for not been incompetent I might never have become an entrepreneur; it was my life experiences that moved me in that direction instead. And I’m certain I would have been far more successful entrepreneur had I had a better startup education and quality mentors along the way.
Still I won’t argue being an entrepreneur is purely experiential. I also won’t argue it’s all in the genes. But I will argue that in the grand scheme it just doesn’t matter.
What matters it that there is almost certainly a huge pool of untapped latent innovative entrepreneurs who could transition into active entrepreneurs launching high-growth startups. As Azeem Azhar wrote on the subject:
There are those who may have many pop out of the womb on the far end of the distribution, but emerge in cultures where the things that can make one an entrepreneur are not valued. The pastiche of this would be the high-performing child who is driven back to the fat-middle of law or consulting by school, college and parental pressure. I am sure this group keeps many a psychiatrist and divorce lawyer in business as they hit their forties and reality dawns.
How about we discuss how latent innovative entrepreneurs can get the encouragement, mentoring and other forms of support that are crucial. And since "swinging the bat" more often results in more "home runs" let’s find ways to minimize the potential of finanical devastation of a simple "strike out"to encourage more prospective entrepreneurs to "swing" once and/or to swing more often. And as Samidh Chakrabarti asserts, let’s get more people to pursue their passions as innovative startups by helping them see it as an option and by providing education in the skill sets needed by would be successes but are only obvious to most after they’ve failed.
So why don’t we stop this fruitless back and forth about nature vs. nuture and (at least from a public policy perspective) instead focus on finding and cultivating latent innovative entrepreneurs?
P.S. Fred Wilson, another VC I greatly admire, wrote on this topic back on the 19th in his post "Nature vs Nurture and Entrepreneurship." I wonder where he stands now on the idea of encouraging more latent innovative entrepreneurs vs. continuing the nature/nuture debate?
Feb 4th, 2010 | Atlanta, Opinion, Startups
Lance Weatherby of ATDC and Socialytics wrote a post today entitled Nobody Told Me where he ranted about how there are too many startup activities in Atlanta and not enough people "creating products, getting customers, and building companies." After writing a long comment which his blog wouldn’t accept for some reason I decided now would be as good a time as any to start blogging again. What follows is the comment I originally wrote for Lance’s blog:
As someone who started a monthly Atlanta Web Entrepreneurs meetup back in Jan 2007 I feel like this post paints a target on my back. Hopefully that was not your intention?
What may not be obvious is I have been agonizing over the issues you described for over two years but not sure how my efforts could evolve to help. At the end of last year I finally realized how my efforts could positively affect execution and as such I made the changes to AWE that I did, i.e. renaming AWE to Atlanta Web Marketers and also launching Startup Atlanta.
First, one thing that I obviously wasn’t able to make clear to you (and others?) was that Atlanta Web Marketers is NOT targeted at Startups and listing it in this context is doing it a disserve. AWM is targeted at small and medium sized businesses, non-profits, government agencies and replicative entrepreneurs, NOT on innovative startup entrepreneurs with a goal of helping them market their products and services better on the web. FYI, there is a huge demonstrated need for people who are effectively operating their organizations to learn how to better market on the web and that’s the market need that AWM is targeting. AWM meetings is all about execution those people in those organizations, and by focusing on that target market it becomes a business itself and running the events are execution. So please take AWM off your list of Atlanta Startup events, as it’s not.
Next, Ignition Alley events are for the most part not startup-specific events either. Some are but most of them are targeting the same market as AWM events. It’s as unfair to list Ignition Alley events as being part of the glut of startup events as it it is to blame people who live and work intown as being part of the metro Atlanta’s rush hour traffic problem.
Continuing, there is Startup Atlanta and it is NOT an event; it is a (soon-to-be) non-profit who mission is to study the ecosystem, identify how to grow it and as much as possible be a catalyst help others execute on on advancing the ecosystem. Yes Startup Atlanta will run the #OnStage event monthly (which I think you misnamed as "OnStartup" in your post), it will run roundtables, it will run task force meetings, and it will probably run other events. However, unlike the former Atlanta Web Entrepreneurs events all of Startup Atlanta’s activities will be measured by how well the activities focus startup entrepreneurs on executing and/or growing the ecosystem support needed by startup entrepreneurs to execute and not providing new ways to waste time.
Specifically let’s look at #OnStage. It’s modeled after the NY Tech meetups that according to those I’ve spoken with in New York has been very effective in driving startup execution in the New York area. It’s an event that can give some local entrepreneurs exposure for their startup rather than how most have toiled in obscurity. As a requirement for presenting at #OnStage startup entrepreneurs must demo their offerings somehow (NO powerpoint) so all those who haven’t executed well enough to have something to show won’t qualify. In addition #OnStage allows the audience 10 minutes of rude Q&A forcing presenting entrepreneurs to be well prepared with a viable business model or to come across looking rather foolish to the community. Finally #OnStage rewards startup entrepreneurs who are doing the best job of executing by selecting winners and getting those winners more exposure which hopefully will mean more customers, partners and/or investors.
Beyond that, Startup Atlanta will only be promoting events that have as a goal to either advance the ecosystem or help startups execute better, and we’ll be focusing on metrics as much as possible.
And while Ben Sabrin and those like him may know all they need to execute well without outside help not everyone who could otherwise execute successfully knows everything they need to succeed. And that’s where targeted, smaller events come in including some we plan for Startup Atlanta. I’ve also noticed that ATDC has a plethora of such events which you didn’t mention including "Circles", "Brown Bags", "Open Coffees", and more. While they too add to the glut of events I actually expect they are of the type that will help startups execute better (well, maybe the first two named and possibly others; though not sure about Open Coffees.)
But while I think while your criticism would have been very well placed about this time last year today it’s a little late because we as an ecosystem have evolved. For example, I understand that StartupChicks is doing some really fabulous events focused on execution for their constituents (but as I lack the requried chromosomes I can’t give a firsthand testamonial.) Capital Lounge has renamed to StartupLounge Atlanta to refocus, according to my memory of my discussion with Scott Burkett, on execution rather than on raising capital. And Startup Gauntlet is focused on perfecting a pitch; again, execution and not something you can repeatedly attend. StartupRiot is as I understand it in large part focused on both getting local attention for startups and gaining attention from investors outside of Atlanta who actually write checks, and that is something many local startups badly need to execute as well. I believe most of these evolved because their organizers identified a need to focus more on results and execution.
And some of the other events you mentioned are industry or technology specific too, not startup-specific. So you do Mobile Monday, AWsome Atlanta, SoCon, and ProductCamp all a disserve by listing them here. (As an aside, you didn’t mention ATLRUG; it’s inline with AWsome so why not? As for ATDC/TAG Entrepreneurs and Venture Pipeline I don’t have enough experience with them to comment.)
So Lance please do get to know the value each event and it’s associated organization has to local startup execution and learn which events are startup-related and which are not. Casting doubt on the value certain activities bring may end up harming the creation of products, the gaining of customers and the building of companies more than it helps.
-Mike Schinkel
Executive Director; Startup Atlanta
Organizer; Atlanta Web Marketers
Partner; Ignition Alley Atlanta Coworking
P.S. Personally speaking, I spent 2007 through 2009 getting to know people in the Atlanta startup community and to build relationships both as an event host and by attending as many related events as I could. I had never done this locally during my prior two decades and my ability to grow my business beyond $12 million annually greatly suffered because of it (and I expect others who rarely or don’t create relationships in the community suffer an inability to execute as well.) But my New Years resolution for 2010 is to focus my event hosting and attendance on only those events that will help achieve the execution goals I’ve set for Startup Atlanta, for AWM, and for myself. To your point Lance, I’d recommend startup entrepreneurs do the same.
Oct 1st, 2009 | Opinion, Social Media, Web
A friend recently sent me a URL via a Forward-to-Friend.com which is a service of MailChimp. While I really love the guys at MailChimp their URLs for their Forward-to-Friend.com are simply awful. There days of social media well designed URLs are finally being recognized by many as being extremely important, but not everyone gets it yet nor does everyone know best practices for designing URLs.
Make ‘em Short and Sweet
One of the traits of a well designed URL is that they can be grokked with a quick visual scan. They should also be no longer than really necessary because one of the more common link sharing sites (Twitter) shortens long URLs automatically. There are many other traits of a well designed URL, some of which are specific to context but if it’s too long and you can’t understand something about the URL by looking at it something is really wrong. And anything that impedes sharing of links is a foolish addition. So I bitched about this URL on Twitter that a friend of mine sent me in email (let’s call her "Jane Smith" and @BenChestnut asked me to clarify. Here’s the URL:
http://us1.forward-to-friend.com/forward/show?u=0fea6c2e08126550f4c318d4b&id=cd941d1fa5
What’s wrong with this URL?
So what’s really wrong with this URL? Let me count the ways:
1.) "us1."
This subdomain seems to imply that its specific to the US which I’m lukewarm on having a subdomain in this context it adds unnecessary characters. And what’s with the "1?" Is there a ".us2?" Is this just a server convenience? C’mon guys, hide that crap the user; they don’t want to know.
2.) "forward-to-friend.com"
Okay, so it’s a cool domain, but you really couldn’t you come us with something shorter than 21 characters?!?
3.) "forward/show"
Uh, one word: "Why?!?"
4.) "?u=0fea6c2e08126550f4c318d4b"
Do I really need to say anything about this? I mean, it’s waaaay too long and how does any of this mean anything to anybody? The only thing is does it make the programmer’s life a tad easier to uniquely identify the user but only on the day it was implemented.
5.) "&id=cd941d1fa5"
Another too long and non-meaningful computer number. The "id=" identifies the URL being forwarded. But does it mean anything?
What would be better?
So here’s a better hypothetical URL with analysis to follow:
http://fwd2.net/janesmith/laura-coyle-at-fernbank
"fwd2.net"
The "fwd2.net" domain is owned by a squatter. Why not pay them a few bucks and pick it up? (or get something similar and short?)
"/janesmith"
Not super short but much like Twitter’s screen name it identifies the links shared by the user who picked the name "janesmith" (i.e it replaces "?u=0fea6c2e08126550f4c318d4b.")
"/laura-coyle-at-fernbank"
Again not short, but as this would be selected by the user before sharing it would be as short as the user wanted it to be. So the user could have picked just "coyle-fernbank" or "fernbank-oct2" or similar. But what is really important is that it is meaningful!
And another benefit?
With this format you also get this URL:
http://fwd2.net/janesmith
At that URL you could have all the links "janesmith" shared when she is logged in, and she could set those shared links to be private or public, or later once more functionality is added the links could be made selectively available to different groups of friends.
Further there could be groups of URLs shared such as anything with a trailing slash could be tagged links, i.e. in this case "jazz":
http://fwd2.net/janesmith/jazz/
Hopefully you can see a tremendous amount can be done with URL design but sadly there are still too few people who pay attention to it. Maybe that’s because there’s no book of best practices. Hmm, might be an opportunity there…
Still think it is unimportant?
And for you skeptics out there who really think that "users don’t look at URLs" take a look at the apps that are succeeding lately, Twitter being a main one. Most of them are designing their URLs well. Coincidence? I don’t think so.
Thanks for asking
Anyway Ben, thanks for asking. Hoping you see the value in it, make the suggested changes, and find that it’s made a positive difference.
Sep 20th, 2008 | Atlanta, Marketing, Web

Last month on the 21st we had a blowout meeting about Twitter for the Atlanta Web Entrepreneurs meetup group I organize; over 100 people attended!
We started out with an Intro to Twitter which I prepared and delivered. It reminded me of delivering training long ago during my DSW Group, Financial Dynamics, and Expert Education days.


Normally we find others to give all the presentations but given how confused some people where at our Facebook meeting when we started with the assumption they knew about it, I decided it was best for me the Twitter newbie to give the other newbies the introduction and then let the "rock stars" in our lineup really get into the meat of things.
We then launched into a video conference with both Wayne Sutton (@waynesutton on Twitter) and the Triangle Tweetup (@triangletweetup on Twitter) as well as Robert Scoble a.k.a. "Scobelizer" (@Scobelizer on Twitter). Loren Norman (@lorennorman on Twitter) of Snowcap Labs did the honors of organizing the video conference and for that we were very grateful. Knowing what a web celeb that Robert is and the subsequent constant demand on his time, we scheduled Robert to speak for only 5-10 minute but instead he spent over 30 minutes answering audience questions. Kudos!

After the video conference we have the took a break and then moved into a Q&A session with Sanjay Parekh (@sanjay on Twitter), Tessa Horehled (@tessa on Twitter), and Paul Stamatiou (@stammy on Twitter) each gave us their perspectives on why Twitter is so invaluable.

As many people said after the event this was one of their very favorite AWE events yet, and I certainly agree; it was right up there. Thanks to all involved including Wayne and the Triangle Tweetup, Robert, Loren, Sanjay, Tessa, and Paul for making this such a great event.
It really is great to have such nice people who are willing to help their peers all here in our hometown of Atlanta GA.
Go Atlanta!
Visit Flickr to see all photos I took for this event.
P.S. Oh, and I almost forgot! Atlanta Web Entrepreneurs is @atlantaweb on Twitter, and I’m @MikeSchinkel on Twitter. See ya in the Twittersphere!
Aug 15th, 2008 | Atlanta, Marketing, Software, Technology, Web

Just an announcement that we are going to be discussing Why you MUST have a Twitter Strategy at Atlanta Web Entrepreneurs on August 21, 2008.
I’m going to present a short intro/overview to Twitter and then, god willing and the creek don’t rise, we plan to have two (2) video conferences, one from Triangle Tweetup and the other from a soon-to-be-announced Industry luminary with over 25,000 Twitter followers!
After the 8pm break we’ll have a roundtable-less discussion and Q&A led by our featured participants:
Anyone that wants to attend should first be sure to have a Twitter account and to follow atlantaweb. We’ll use that list as a roll call for the meeting and we’ll announce our special guest on the atlantaweb Twitter account by 6pm Wednsday August 20th.
For more details and to RSVP see go here.
Aug 6th, 2008 | Marketing, Web
It’s been almost fifteen years now since the web first hit it’s tipping point and transitioned from an academic’s playground and a mere curiosity for the average person to the decidedly mainstream global change agent that now drives trillions of dollars in global value creation annually. During that time we’ve gone from asking "What’s this ‘World Wide Web’ thingy the geeks keep talking about?" to rapidly seeing Web-based services dominant the activities of practically every business person alive. In the days of the first Internet "gold rush" a.k.a. the "dotcom bubble" it seems that everyone and their brother grabbed a .COM domain, or ten, and set out to strike it rich. Back then you really didn’t even consider getting anything besides a .COM for your website business but that was okay because many good brand names could be created from still available .COM domains.
Fast forward a decade and great .COM domain names have became scare and even good .COM domain names are hard to come by especially with all the domain squatters. In addition a lot more top-level domains have opened up, and many small countries such as Tuvalu (.tv) have decided to cash in on their (un)natural resources. And just as fashions change, some creative types who needed a good domain name chose to forgo the .COM status quo and the "www." sub-domain convention and instead compose domain names from words by ignoring the domain level separators (i.e the periods ".") From this trend popular websites with domain names like http://del.icio.us (aka "delicious") were born.
If you are not familiar with delicious it is essentially a website to store your web bookmarks, but storing them "in the cloud" as opposed to in your browser. And one of it’s best innovations, since mimicked by thousands of other sites, it the ability to allow users to categorize with freeform "tags" and later recall their bookmarks by the tags they assigned. These tags are just words, any words your chose, such as "marketing", "video", "php", "bestpractices" or even "shoes." What words you use to tag with is totally up to you.
I’ve been using delicious for several years now and at this point not a day goes while surfing the web that I don’t tag at least one website for future reference. You can even use it to create lists of sites groups by a tag and then send those links to others so they too can see your list of links. But I digress; there is a lot more to delicious but the subject of this post is the .COM domain so we’ll my detailed description of del.icio.us for another day.
As delicious got more popular with the many influencers on the web, Yahoo stepped in and bought them. Since then delicious has languished for years, still there but never updated. Probably the best thing that has happened to delicious during that period was Firefox built delicious tagging into their browser as their favorites list — if you choose to let Firefox use delicious for you — and the fact that since it hasn’t changed it’s been a pretty stable target for people who wanted to use to delicious API to create add-on functionality and integrations.

So, after years of languishing it turns out Yahoo has been paying attention to delicious behind the scenes, and behold; there is a new delicious! What’s more, Yahoo has redirected all attempts to access delicious at http://del.icio.us to instead find delicious at http://delicious.com/ and thus, in one fell swoop, have extinguished the quirky domain name that was in part why the web’s tastemakers first took note of delicious. The powers that be at Yahoo probably choose to do this because of usability data I expect they’ve collected that probably told them that the "in crowd" got the funny spelling but that the vast majority of users were simply confused.
Which brings me to the crux of my post where I posit the following:
Are .COM domains still required for commercial success in a mainstream website? Or or those rushing to get domain names with all the new top level domains simply exercising futility? Were all these idiosyncratic domain names merely a fad and now we’re back to business with .COM, or did Yahoo jumped the shark on this one?
So what do you think?
Aug 3rd, 2008 | Opinion, Technology, Web
Damon Clinkscales blogged about Twitter Spam last month where he advocated proactively cleansing one follower’s list of "follow spammers" to help reduce the load on Twitter, improve Twitter’s reliability, and increase the value of the Twitter community in general.
I agree!
Still, I think Twitter could take a proactive step reasonably easy that would make it so we don’t have to. I think Twitter could reduce most of the type of Twitter follower spam I got today by applying two simple criteria (And I think Damon also got that same spam today. BTW, nice blog theme Damon! ;-)
I think a strong indication of Twitter follower spam is simply:
- Their following/follower ratio (or their ing/er ratio for short), and
- Their follow rate (i.e. how quickly they follow someone after that last time they followed someone.)
This spammer I got today followed me with 4 different Twitter accounts within a few minutes and each account had around 2000 followings and just over 10 followers making their ing/er ratio about 20-to-1 and I’ll bet their followers were all auto-followed. It’s also clear from the fast & furious tweets that I was not their only mark.
I think it would be reasonable for Twitter to auto-block anyone with a ratio of greater than 15-to-1 ing/er ratio. Twitter could even remove the auto-followers from the calculation; those that follow within around 90 seconds of being followed wouldn’t count as a follower. Doing this Twitter would still give someone the ability to follow 15 people for every one that follows them, and heck they could give them their first 150 people[1] for "free" (i.e. not counting against the limit.) If someone really wants to follow 15,000 people they need to be interesting enough to have at least 1000 people follow them. Shouldn’t be that hard…
Also, Twitter could limit followings per day to, say, 75. That should be enough for anyone, even the most hard-core twitter newbie (150 "free" + 75 more), and it’s not unreasonable to require a newbie to wait a few days to follow lots and lots of people.
If I were in charge of setting these limits, I’d set the ing/er ratio to 5-to-1, give them only 25 "free" and then limit to 25 followings per 24 hour period, but I shot high because I was trying to be "reasonable." Of course, Twitter could allow for special cases by allowing people to request to have those limits manually raised if they provide a good justification for it.
What do you think? Would this work to reduce most Twitter follow spam? I think so.
Aug 2nd, 2008 | Programming, Software, Web
So I’ve got a project where I need to have a Flash component built in Flex to call to a WordPress blog and get information about it’s latest post. Should be no problem right? The easy way to do this would be to just write a “rest.php” file and brute-force all the setup that WordPress does but I thought it would be so much more valuable to implement this as a plug-in.
I figured that I’d just quickly learn how to build a WordPress plug-in and create one for exposing RESTful web services; after all with a year of programming Drupal modules WordPress’ plug-in API can’t be that hard, right? Well turns out it wasn’t that easy and I think I have run into a design limitation with WordPress and I’m beginning to wish I’d just taken the brute-force approach and said to hell with writing a plug-in.
Although I am not 100% certain, and I hope someone can point out that I’m just doing something wrong, it seems sadly like I’m pushing the edges of the WordPress API and exposing where it’s design falls short. By the way, I’m working with WordPress v2.5 because why upgrade mid-project when god knows if WordPress will release another in the remaining days before this project is done and I’ll just have to do again before deployment?
Here’s the details. I started writing a plug-in called “RESTful Services” with a goal of implementing URLs that behave in the following fashion; {format} could potentially be html, xhtml, json, xml, rss, atom, etc.:
- http://example.com/services.{format}
- Provide a list of RESTful services in specified {format}, defaults to html
- http://example.com/services/{service}.{format}/{data}?{params}
- Provide a RESTful service in specified {format}, defaults to html, with optional provided data and parameters.
But before I got all those options working I just wanted to service a page from my RESTful Services plugin where Content-Type: text/plain. I found this page that professes to explain how to hook into the URL routing and after a few fits and starts I can came up with the following code for my plugin that would indeed response to my http://example.com/services URL:
wp-content/plugins/restful-services/restful-web-services.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
| add_action('init', 'restful_services_flush_rewrite_rules');
function restful_services_flush_rewrite_rules() {
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
add_filter('generate_rewrite_rules', 'restful_services_add_rewrite_rules');
function restful_services_add_rewrite_rules( $wp_rewrite ) {
$new_rules = array(
'services' => 'wp-content/plugins/restful-services/rest.php',
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
} |
The problem with the above was that it wouldn’t call “wp-content/plugins/restful-services/rest.php“; it would simply continued to call “index.php” and display the home page! After literally hours and hours of debugging with my trusty PhpEd IDE & debugger I was able to find that the code on lines 737 & 738 of “wp-includes/query.php” told WordPress that my service was the home page! It is almost seems like the “generate_rewrite_rules” was implemented as “a good idea” yet no testing has ever been done on it because for the best I can tell it doesn’t work. (Note I’ve reformatted the code to multiple lines so that it is easier to read and does not extend past the right margin of my blog):
wp-includes/query.php:
if ( !( $this->is_singular
|| $this->is_archive
|| $this->is_search
|| $this->is_feed
|| $this->is_trackback
|| $this->is_404
|| $this->is_admin
|| $this->is_comments_popup ) )
$this->is_home = true; |
I could possibly hack it to get past this by setting one of those to “true”, but none of them are really appropriate; there is nothing there quite like an “is_service” instance variable. Setting something like “this->is_singular” or “this->is_feed” might work but it could manifest incompatibility problems with other plugins or future versions of WordPress. Frankly it was rather disappointing to discover this because it tells me that WordPress has hard-coded all the potential scenarios and doesn’t really have a way around it. Seems to me there should really be a hook here and the type of pages should be allowed to be expanded by plugins rather than be hardcoded as only one of ’singular’, ‘archive’, ’search’, ‘feed’, … and ‘home.’
Anyway, where this manifests itself is “wp-includes/template-loader.php” file which I have included in it’s entirety below. It is on lines 24 and 25 where the template loader loaded the home page because it’s not possible to specify otherwise:
wp-includes/template-loader.php:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| /**
* Loads the correct template based on the visitor's url
* @package WordPress
*/
if ( defined('WP_USE_THEMES') && constant('WP_USE_THEMES') ) {
do_action('template_redirect');
$is_home = is_home() ;
if ( is_robots() ) {
do_action('do_robots');
return;
} else if ( is_feed() ) {
do_feed();
return;
} else if ( is_trackback() ) {
include(ABSPATH . 'wp-trackback.php');
return;
} else if ( is_404() && $template = get_404_template() ) {
include($template);
return;
} else if ( is_search() && $template = get_search_template() ) {
include($template);
return;
} else if ( is_home() && $template = get_home_template() ) {
include($template);
return;
} else if ( is_attachment() && $template = get_attachment_template() ) {
remove_filter('the_content', 'prepend_attachment');
include($template);
return;
} else if ( is_single() && $template = get_single_template() ) {
include($template);
return;
} else if ( is_page() && $template = get_page_template() ) {
include($template);
return;
} else if ( is_category() && $template = get_category_template()) {
include($template);
return;
} else if ( is_tag() && $template = get_tag_template()) {
include($template);
return;
} else if ( is_tax() && $template = get_taxonomy_template()) {
include($template);
return;
} else if ( is_author() && $template = get_author_template() ) {
include($template);
return;
} else if ( is_date() && $template = get_date_template() ) {
include($template);
return;
} else if ( is_archive() && $template = get_archive_template() ) {
include($template);
return;
} else if ( is_comments_popup() && $template = get_comments_popup_template() ) {
include($template);
return;
} else if ( is_paged() && $template = get_paged_template() ) {
include($template);
return;
} else if ( file_exists(TEMPLATEPATH . "/index.php") ) {
include(TEMPLATEPATH . "/index.php");
return;
}
} else {
// Process feeds and trackbacks even if not using themes.
if ( is_robots() ) {
do_action('do_robots');
return;
} else if ( is_feed() ) {
do_feed();
return;
} else if ( is_trackback() ) {
include(ABSPATH . 'wp-trackback.php');
return;
}
} |
Still another problem in this puzzle is the $wp->send_headers() method shown being called here on line 293 of “wp-includes/classes.php”:
wp-includes/classes.php:
290
291
292
293
294
295
296
297
298
| function main($query_args = '') {
$this->init();
$this->parse_request($query_args);
$this->send_headers();
$this->query_posts();
$this->handle_404();
$this->register_globals();
do_action_ref_array('wp', array(&$this));
} |
The problem with the $wp->send_headers(), also from “wp-includes/classes.php”, is that it seems to have the option of either serving an HTML content type on line 183 and 185, or a content type based on a feed (the content types for the feeds are set in their respective “wp-includes/feed-*.php” files) but no custom content types as far as I can determine as there seems to be no way to override calling this function or the logic path contained within:
wp-includes/classes.php:
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
| function send_headers() {
@header('X-Pingback: '. get_bloginfo('pingback_url'));
if ( is_user_logged_in() )
nocache_headers();
if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {
status_header( 404 );
if ( !is_user_logged_in() )
nocache_headers();
@header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
} else if ( empty($this->query_vars['feed']) ) {
@header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
} else {
// We're showing a feed, so WP is indeed the only thing that last changed
if ( !empty($this->query_vars['withcomments'])
|| ( empty($this->query_vars['withoutcomments'])
&& ( !empty($this->query_vars['p'])
|| !empty($this->query_vars['name'])
|| !empty($this->query_vars['page_id'])
|| !empty($this->query_vars['pagename'])
|| !empty($this->query_vars['attachment'])
|| !empty($this->query_vars['attachment_id'])
)
)
)
$wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';
else
$wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';
$wp_etag = '"' . md5($wp_last_modified) . '"';
@header("Last-Modified: $wp_last_modified");
@header("ETag: $wp_etag");
// Support for Conditional GET
if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
$client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
else $client_etag = false;
$client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);
// If string is empty, return 0. If not, attempt to parse into a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
// Make a timestamp for our most recent modification...
$wp_modified_timestamp = strtotime($wp_last_modified);
if ( ($client_last_modified && $client_etag) ?
(($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :
(($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
status_header( 304 );
exit;
}
}
do_action_ref_array('send_headers', array(&$this));
} |
Still, I was able to come up with a solution although it is so very hackish. My solution was to hook the “template_redirect” action on line 7 of “wp-includes/template-loader.php” (see code from that file above.) Though it seems to works thus far, my solution just feels wrong for the following reasons:
- It ignores the fact that WordPress continues to think that my web service URL is the home page,
- It first lets “$wp->send_headers()” set the content type before it overrides it,
- It uses an “exit” rather than a return to keep WordPress from serving up the home page template, and
- It doesn’t use the routing mechanism apparent built into WordPress (see “null” on line 30 of “wp-content/plugins/restful-services/restful-web-services.php” below, I assume it should have been the URL of the .php file I plan to execute but WordPress doesn’t see to use what I put there.)
The function “restful_web_services_exec_service()” is what is called to execute the appropriate web service:
wp-content/plugins/restful-services/restful-web-services.php:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| /*
Plugin Name: RESTful Web Services
Plugin URI: http://mikeschinkel.com/wordpress/restful-web-services/
Description: This plugin enables REST-based API web services for WordPress.
Author: Mike Schinkel
Version: 0.1
Author URI: http://mikeschinkel.com/
*/
define('RESTFUL_WEB_SERVICES_DIR', dirname(__FILE__));
define('RESTFUL_WEB_SERVICES_URL_PATTERN','services(/?.*)?');
$abspath = trim(str_replace('\\','/',ABSPATH),'/');
$rest_services_dir = str_replace('\\','/',RESTFUL_WEB_SERVICES_DIR);
$rest_services_path = trim(str_replace($abspath,'',$rest_services_dir),'/');
define('RESTFUL_WEB_SERVICES_PATH', $rest_services_path);
// NOTE, See: http://codex.wordpress.org/Custom_Queries#Permalinks_for_Custom_Archives
add_action('init', 'restful_web_services_flush_rewrite_rules');
add_filter('generate_rewrite_rules', 'restful_web_services_add_rewrite_rules');
add_action('template_redirect', 'restful_web_services_exec_service');
add_action('init', 'restful_web_services_flush_rewrite_rules');
function restful_web_services_flush_rewrite_rules() {
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
add_filter('generate_rewrite_rules', 'restful_web_services_add_rewrite_rules');
function restful_web_services_add_rewrite_rules( $wp_rewrite ) {
$new_rules = array(
RESTFUL_WEB_SERVICES_URL_PATTERN => null,
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
function restful_web_services_exec_service() {
global $wp;
if ($wp->matched_rule==RESTFUL_WEB_SERVICES_URL_PATTERN) {
if ($wp->request == 'services') {
header('Content-Type: text/plain');
print 'TODO: Generate a list of RESTful Web Services here for this WordPress Blog.';
} else {
list($dummy,$service_name) = explode('/',$wp->request);
if (file_exists($service_php = (RESTFUL_WEB_SERVICES_DIR . '/services/' . $service_name . '.php'))) {
include_once $service_php;
} else {
header('Content-Type: text/plain');
status_header(404);
print '404 - Service not found.';
}
}
exit;
}
}
|
You’ll note that my function “restful_web_services_exec_service()” is very bare-bones at the moment serving only a plain text message “TODO:” for the path http://example.com/services, and assuming that any path http://example.com/services/{service} will execute a same-named .php file in the services subdirectory, i.e. for http://example.com/services/latest-post it will look for “wp-content/plugins/restful-web-services/services/lastest-post.php” and then delegate all the work to that .php file.
wp-content/plugins/restful-services/services/latest-vidclip.php:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| /*
Filename: latest-post.php
Service Name: Latest Post
*/
global $wp_query;
$post = $wp_query->post;
$file = get_attached_file($post->ID);
if (empty($file)) {
list($file)= get_enclosed($post->ID);
}
$charset = get_option('blog_charset');
header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
$link = get_permalink($post->ID);
$html = < <<POST
<?xml version="1.0" encoding="$charset" version="1.0">
<post id="{$post->ID}">
<title>{$post->title}</title>
<video>$file</video>
<link>$link</link>
</post>
POST;
print $html; |
Here is an example output returned by calling http://example.com/services/latest-post:
1
2
3
4
5
6
| < ?xml version="1.0" encoding="UTF-8"?>
<post id="2">
<title>Sample Post #1</title>
<video>http://videos example.org/video1.flv</video>
<link />http://example.org/sample-post-1/
</post> |
While bare-bones, that will meet my needs for the moment since I really only have the need for one service that responds to the HTTP GET verb. However, I can see where I might end up fleshing this out and create lots of helper functions that would streamline the creation of web services for RESTful access to the entirety of the WordPress database. If I do so I’ll be happy to donate this plugin to the community. In the mean time if you’d like to use this for your own use feel free but caveat emptor. However, if you’d like to use this code to create a plugin to contribute to the community, please contact me for collaboration. Of course if you’d like to retain me to fully flesh it out for you, I’m always open to that too! :-)
Finally, if anyone who knows the WordPress API better than I do can tell me where I erred in my analysis, and I really hope I did, please let me know so I can architect this thing better. On the other hand, if I was spot-on in my analysis maybe this will help the WordPress team understand what needs to be done so they can empower WordPress to generate any arbitrary content type without having to resort to hacks or bypassing the WordPress index.php startup code.
Aug 1st, 2008 | Atlanta
Recently on Twitter I’ve had several people ask where they could find another good place for free WiFi in Atlanta. Each time I answered so I decided to maintain a page on my site with a list of the Atlanta WiFi Hot Spots I go to frequently or at least know about.
Enjoy.