2018-02-16T10:54:49-08:00http://joelhooks.com/Octopress2018-02-15T13:30:00-08:00http://joelhooks.com/blog/2018/02/15/how-we-work-at-egghead-dot-ioegghead.io is a platform to empower humans, built to challenge the status quo of how we work and learn new skills.

We produce high quality, concise web development screencasts created by expert open-source contributors and other talented professionals. While we offer a lot of this content for free, there is a membership fee to access exclusive content.

As a business, egghead.io serves as accounting, marketing, and delivery platform for technically minded content creators. We enable creators to distribute short-format educational videos and various supplemental materials. We pay instructors monthly royalties that are a percentage of membership dues collected and the relative time spent by members viewing their lesson videos.
To accomplish this, we build software with two primary goals:

Create badass content with egghead instructors to help them focus on the creation of content so that they can earn royalties and not worry about the rest of the process.

Deliver badass content to egghead members to improve their technical skills and stay up to date with the latest and greatest the web development world has to offer.

The two are related and overlap quite a bit, but are also very different business tasks with two distinct groups of customers.

We are confident that our best work has yet to come.

We know this because we are agile.

For us, agile means “incremental, iterative, and collaborative”– not Agile™ Certified Scrum Master.
Our goal is to make sure everybody building egghead is well challenged and interested while growing value for our customers (members & instructors).

We believe that process evolves through discussion, experimentation, and lightweight documentation.

The idea of small improvements applied consistently over time is etched into the foundations of our business model. From the videos that we create to the software we deploy.

We work remotely. Official business hours are 9ish-5ish Pacific.

Written communication isvitalfor a remote business, which means both reading and writing. Everybody works whenever and wherever so we end up typing at one another quite a lot.

Tone, context, intent are essential when we write. We have to respect one another as humans, give people the benefit of the doubt, and also respect everybody’s finite amount of time and attention.
We don’t have standing meetings or follow a strict process for checking in. We have a devoted channel in Slack that we regularly share what we are currently working on.

Slack is instant, but transient. Slack is a great tool for at the moment collaboration. It’s the water cooler, or often like knocking on somebody’s office door. Conversations in passing and the moment. It’s often a very real distraction. Almost a hindrance.

Email is often better when you need something. It’s asynchronous, and a well-titled email can be the start of valuable threaded conversations that persist and are easy to find later. Email is immutable, searchable, and functions well as an extension of our brains.

Code reviews on Github, documents in Dropbox Paper, and InVision comment conversations continue alongside the work that we are creating. Persistent, well maintained, living documents that describe the work that we are doing in detail.

The component is our fundamental “unit of work” that we focus on for both design and development. We are building complex structures out of modular components. The component first approach is heavily inspired by “Thinking in React.” Often this means we are working with related sets of components, which we often refer to as pages or screens.

We try to get our work into the hands of our users, both paying members and instructors, as early as possible. We intentionally avoid big releases with a lot of fanfare, instead of pushing early and often as close to continuously as possible.

We want to measure impact. We don’t mind “throwing away” work or admitting “failure.” We work using themes, and we aren’t obsessed with deadlines or estimates. In fact, we don’t use either timetables or forecasts as a development tool.

We tweak what is there, and try not to use the past as a significant constraint of the future. We take time to improve work that is “done” because it is never “done” and we can always get better.
The mechanics of how we accomplish this will evolve to meet the needs of the team. The designer/developer workflow is always a challenge, and we will attempt to bridge that gap and keep agile principles at heart. That workflow might look something like this:

handful of current themes are identified and agreed upon

user stories are created to describe the functionality and impact of improvements based on those themes

Initial constraints and goals get established

Low fidelity wireframes are shaped to define structure and feature set

Development begins using the wireframes as reference

Higher fidelity comps get designed, generally using the essential established components

Components that aren’t created yet get identified and receive design focus

Individual components are developed and deployed as soon as viable

We stop, take a breath, consider thoughtfully before returning to step 1 above

When work has commenced, we continue to discuss the design work. The design remains open to enhance and iterate as development exposes questions or edge cases.

During work, we identify major/primary components. Important “linchpin” components will receive attention and become part of the overall inventory of components assets.

Design begins with simple wire-frames that represent the structure of screens and components. These UX focused wires are presented and discussed before being implemented.

The wires make the process of designing components more focused on behavior, and less on visual aesthetic. We can quickly “make the designs go” to gain an understanding of how they feel to use.

The functional wires can them then be deployed to share, test, and discuss before high fidelity finished design work occurs.

The finished designs are implemented in code on top of the functional wires, adding consistent style and interactive flourish.

We want to experiment and play. Keeping it our work small reduces risk and allows us to try more things. Most of the time this works great, but sometimes we will admit when something isn’t working, learn our lessons, and move on.

Code review is a requirement for (most) code that deploys to production.

We embrace that the value of code review is sharing knowledge.

The code creator is the first person to review the code, adding comments that inform the rest of the team of the “what, how, and why” of the code they have written.

Reviewers ask questions so that they can understand the work. Code review is a form of critique, but the goal is to learn.

“Done for now” is perfectly fine.

Finishing is important. If things are going well, we will never be “done,” but we aren’t seeking perfection. It’s better to release our creations than it is to hold them back until they are utterly polished. Continuously publishing our work helps to ensure that we build the right things that match what people expect and need instead of guessing.

We are still putting out excellent work, but just that we understand that as a business we are here for the long haul and that working small gives us the ultimate flexibility to create something spectacular over time.

Like everything we do, this approach documented here is subject to change as the business changes. Opportunities appear and disappear all of the time, and we want to be open to exploration.

]]>2018-01-01T09:38:00-08:00http://joelhooks.com/blog/2018/01/01/what-i-learned-and-did-in-2017Make all the big life changes at once…

In 2016 we moved our family of 6 out of our home of 15 years across the country to settle in Vancouver, WA. That’s quite a lot of change for everybody involved. My partner lived her entire life in Fort Worth, TX. Her family was, for the most part, within a 20 mile radius of our house.

It’s hard to even begin to describe the level of trust and commitment that this amazing human has given to me. ❤️

Shortly after arriving in the Pacific Wonderland, we made another decision, and on Feb 9th, 2017 she arrived. Our little Romi π.

HOLY SHIT 5 FUCKIN KIDS ARE YOU KIDDING ME LOL??

They say you should make and execute as many major life changes as possible in as short a time span as you can, right?

Hah.

They don’t say this, because it is a lot. At this point I can’t even recommend that people have kids, much less 5 of them.

I wouldn’t change a thing though, and this little firecracker of a baby girl makes me smile every time I see her.

They say the Lord giveth…

One of the reasons I love living in the PWN is the closeness of my family. After 36 years in Texas, we were finally close enough to casually visit them. In the summer of 2016 we were able to spend a full week lounging at Loon Lake in Washington with my cousin Lizzie and her family.

It was wonderful. We looked forward to doing it year after year.

On March 2017 Lizzie was killed in a sudden and random car crash.

Aunt Mary, Uncle Dave, Sarah, Jim, Luke, Andrew, Mable, Theo, and all the other people that cherished Lizzie and her love of life…

I love you.

I didn’t want to cry today, but here I am.

Hug them.

Back to work

Through all of this joy and pain, I’m privileged to have a stable growing business, that continues to provide a living wage for my family. The stability has meant that I’ve been able to be there for my partner in a more meaningful way than I was with our previous 4 kids.

It means that when I was sad for a month, I could step away and take care of myself and my family without having to worry about how we would pay rent.

This year egghead has grown. We saw about 20% growth in revenue, and have added several amazing team members. We were able to afford 100% health coverage for our full-time employees and their families. This was huge for us, because having to worry about these things can be a real emotional drain.

The biggest challenge for me personally, as the director/orchestrator, has definitely been the team building. For the second year in a row I got it wrong and had to correct my mistakes. It’s painful. It feels cruel. It makes me personally ashamed. It makes me sad.

But that’s part of the deal. And I have to analyze what when wrong, and how I can correct the problems for the future in a way that saves people the turmoil that sudden job loss causes.

It has to work for me. The job has to satisfy me. I have to be selfish. I have to protect myself. I have to build something strong and resilient that can keep me entertained and fed for decades.

This has to be balanced with providing the same for others of course. If this thing that we are building is an “engine of opportunity”, a thing that provides amazing jobs to as many people as possible, it has to do that for me first.

Leadership is a learned skill, like almost everything. It’s hard af, and the only thing I can do with the failures is try to learn from them so they don’t get repeated.

Doubling down on what works…

egghead has evolved quite a lot in since John and I started it in 2013. In the beginning it was John & Joel, but it quickly evolved into a platform for content creators (instructors) to publish their learning material and let us take care of marketing, delivery, and support.

The paying members of egghead are ostensibly our customers, but our true customers are the content creators.

Much of the year has been spent improving the experience for egghead’s instructors so we can

make the opportunity to instruct on egghead more accessible to more people

increase average instructor revenue in a meaningful way

provide a more consistent learning experience for egghead members

Step one was to provide a written introduction and guide for new (and existing) instructors to reference. The guide is a living document that we are able to reference and amend as the process evolves over time. We wrote it free and open to the world in the hopes that others might be able to benefit, regardless of how they choose to publish their work.

On top of this we did extensive work building software tools that provide a more consistent and clear experience for instructors when they publish their works.

It wasn’t too long ago that I asked them to email me a link to a Dropbox folder and then manually did the rest. heh

React ❤️

In 2017 we made massive changes to our website and infrastructure, fully embracing the React JavaScript framework for our user interface.

It’s been wonderful. Robust. Delightful to use.

At the end of the year we have a massive library of components across many many pages in production. It is easy to maintain and add new features.

I’ve been developing UIs for 20+ years and React is by far the best developer experience I’ve had.

It’s great. We pair it with MobX. Also great.

Throwing away perfectly good code.

We approached this initially with an entirely different instructor website, but this idea ended up being confusing. There was on “single source of truth” to refer to, and it split development efforts across two distinct products.

After spending 6 months and many tens of thousands of dollars building out this multi-faceted approach, I had to make the tough call to kill the entire thing, effectively throwing all of that hard development and design work into the bin and starting fresh with a consolidated single-site approach.

That’s a hard decision to make, but it was the right one. Now we are on track.

UX Research

We’ve got the honor of working with a handful of amazing consultants. The biggest area that we choose to invest in is research.

User experience often gets lumped in to UI development and treated as a visual exercise.

Reality is that good user experience starts with research.

We need to understand our users and their needs.

For egghead, this is two-fold. We want to understand what our members need, and we absolutely need to understand what our instructors need.

So we hire professionals to help us.

This has worked out extremely well, and has lifted everything that we do. We aren’t guessing, or using our intuition for every decision. We still guess and intuit, but we can now often refer to well executed research to feed and support the decisions we make.

High quality UX research is very expensive, and when you are bootstrapping a business, it can feel like it might be a waste.

“Shouldn’t we spend this money on development and get features out the door?”

But, every time we’ve done this the results for us have been overwhelmingly positive.

The trick is to actually use the research! Because it is such a large chunk of our operating costs, we deeply respect the work and use it to drive design and development in a direct and tangible way.

Knowledge is power. Knowledge is confidence.

More please.

We started a podcast.

My favorite show of 2017 has to be The Hot Ones, which is an interview show on YouTube where they eat progressively hotter chicken wings and ask famous people questions with each step. It’s great.

There was an episode that featured Gary Vee, who’s a generally inspiring guy that has some serious skills making money online. He told Sean that it was crazy that he didn’t have an audio version of the show, and it was a click moment for me.

I hopped on Slack and told John, “we’ve gotta do a podcast”. His response:

We made it easy on ourselves and used the excellent PodcastMotor service, which is the rondo rotisserie grill of creating podcast. Coupled with Zencaster, it takes all the grueling production work out of the picture so John and I can focus on interviewing guests.

Millions of emails

I sent somewhere in the neighborhood of 10,000,000 emails in 2017. They all are attached to my work email and “Joel from egghead” is the sender.

This has been, and continues to be, extremely lucrative. Most of these emails are content announcements. I’m letting people know that there is new stuff to watch. Everything we release is released free for a week before becoming member’s only, so everybody has the chance to learn something.

2.5 million of those emails were sent in December for our end of the year extravaganza. We released 18 courses over 12 days. On the final day we sent 4 emails, and a last last last chance email on the day after the promo ended.

The last last chance email resulted in $50k in additional revenue, but it also pissed off at least a dozen angry men that sent me hateful responses.

I can live with that.

For the month of December 2017 we are cutting almost $250,000 in royalty checks to our instructors. A quarter of a million dollars into the pockets of hard working developers and open source contributors.

Feels good. Really good.

It wasn’t all work. Lots of fun too!

Outside of work and the big life events, I had a lot of fun in 2017.

Ink

Once you get over the fact that you’ve defaced your arms forever, it’s way easier just to do more.

My approach to tattoo is to pick great artists and let them do what they want. I thematic suggestions, but every tattoo is part of an art collection that I carry around with me. It’s an expression of the artist, and not a deep meaningful personal milestone.

The abstract colorful pieces are done by Winston the Whale in PDX. The wrapping undead crystal space dragon was done by Joby Cummings.

I’ll probably get more.

Wheels

I traded in my beloved 2015 Subaru Impreza STI.

It’s such a great vehicle. Fast (enough), responsive, comfortable, and excellent in all road conditions. The spoiler is even useful when you are going too fast on winding roads (at the track, hopefully).

The intent originally was to hit the track more often, but after the one and only track event I participated in, I learned that it wasn’t for me. It’s a lot of work, very expensive, and has a non-trivial risk of death. It’s also not fun for the whole family.

So, I got a Jeep.

I’ll be honest, driving the jeep around town isn’t much fun. It’s a truck. It’s heavy. It’s slow.

Every other vehicle in this reason is a Jeep.

But, it’s a shit load of fun driving on the trails.

We are able to explore the backwoods that this wonderful region has to offer. We can go places that are hard to get to and see spectacular sights.

This Jeep is no fuckin’ mall crawler.

Good times.

Woodworking

Working with wood is fascinating. From a tree to a useful object. It’s something I’ve wanted to do for a long time, and in 2017 I was able to try it out. I took a class at a local maker space in Portland (ADX), and built a small table.

I finished the table early so I spent the last night of class constructing this joiner’s mallet.

Now I’m planning my garage shop, so I can dive in and make useful things from wood on a regular basis.

Self-care

I didn’t gain much weight. At 43, I worry about death a lot. Being overweight contributes to that. I’d like to lose weight in 2018. I know how to lose weight. I’ve done it before. Candy is delicious… 😭

For the first time in 30+ years I don’t have to wear corrective lenses! I got LASIK. It’s awesome. DO IT. 100% worth it.

Team gatherings

egghead is 100% remote. That means we don’t see each other in person very much. This summer we got together in Utah with most of the team.

Living near Portland is nice because people actually visit here, unlike Fort Worth.

I got to prove to Taylor that his job at egghead wasn’t an elaborate catfish.

And John and I finally had the opportunity to converge our families.

Next year we are going to gather in Prague. Can’t wait!

Art Camp

For the last several summers I’ve gone to Aspen to Anderson Ranch to have Joshua Davis, the Dark Wizard of computer generated art, school me in his craft. In 2017 we worked on Arduinos, and I learned a ton. In 2018 we are going to do computer vision. Can’t wait.

THE PATH OF TOTALITY

Holy shit. Total solar eclipse is really really amazing. If you’ve got the opportunity to see it, do it. Fuck the traffic. Sit your ass in the long line of cars and witness this event.

2018 Plans

More of the same, really. We are going to keep doing what we are doing, incrementing, improving. Small changes. Nothing radical.

For the first time we’ve got a solid 1 year plan for the business based on research.

We aren’t going to have any more kids, or move across the country. We might get to travel a bit.

It should be nice.

]]>2017-10-26T13:09:00-07:00http://joelhooks.com/blog/2017/10/26/why-we-hire-consultants-to-help-build-egghead-dot-ioOver the past few years we have built egghead.io from a relatively simple “video blog” and into a full blown platform for developers to publish their knowledge about software development as screencasts and get paid for it. The process has been intense! Every step of the way was a new road block labeled “NOW LEARN THIS”. Often with multiple new skills to learn at the same time. If you stop for a minute and consider what it takes to build an online business from scratch, your list would be similar to:

Serverside Development

Frontend (UI) Development

Internet Marketing

Business

Accounting

Design

User Experience

Support

Analytics

Communications

Copywriting

…?

It’s really fucking daunting. As a “mild” perfectionist and planner with symptoms of OCD to proceed without first fully understanding the problem and knowing the proper, best-we-can-manage solution to the problem. To solve this, especially in the beginning when it is just you and the wide open internet is to:

Buy all the books.

Read all the books.

Search for answers and opinions on the internet.

Combine all of the above into a composite solution for your specific problem.

This takes a long time, depending on the scope and difficulty of what you want to build. It is required though, and as somebody that is trying to solve a non-trivial problem using software deployed to the internet you will need to be an expert in many things.

Wikipedia, Amazon, Kahn Academy, Safari Online…

It’s seriously amazing to be alive in this time of deep deep wealth of available information.

There are definitely other options. You can even take college courses, but that is dreadfully slow and it will delay making The Thing for sure, maybe forever. They don’t offer a degree that combines the list above, and it is very expensive.

Another option can be to take on a partner or partners. This is tricky, but you’ll often hear the story of a technically oriented person teaming up with a business savvy person to create a thing. Partnerships can be amazing and work out wonderfully, but they can also be horribly tragic and painful. Choose your partners well, and understand that (maybe most?) partnerships can end badly if expectations don’t meet reality.

Some combination of the above is going to be how you get started building your business. You’ll understand the principles of getting an audience built. Once you’ve got a group of people that appreciate your help and knowledge, you’ll sell them something small. Now, you’ve started to develop an income somewhere in the “hey, this pays the rent!” range, and you’ll have had interaction with your audience and understand their pain and suffering even more. Now it’s time to alleviate the pain, and build The Thing.

What’s The Thing? Who knows. If you start with an idea of the solution, you’ve basically tainted the whole process and will most likely become another sad case of “9 out of 10 small businesses fail” giving the rest of us a bad name. Right now what it is doesn’t matter, but the process remains the same.

Learning all of the skills needed to build a Platform of Audience Pain Relief at an expert level is an admirable goal. For a lifetime. That’s too long, so how can we speed that process up and serve our audience in more profound ways sooner?

Expert consultants. Individuals that bring years of experience, intelligent insight, and high level focus on the specific problems that your business craves. Consultants, good consultants, are highly paid to give you their undivided attention. The amazing world-class experts in their fields that would take you a decade of sad failures to fully understand what they can accomplish in a matter of weeks.

A good consultant will cost you thousands or tens of thousands of dollars per week, and when you see the proposal’s last page, it can be a bit of a gut shot. A good consultant can ultimately save you years and increase your earnings exponentially.

If you want to maximize the value you get out of your consultant engagement, it’s important to have a conversational knowledge of the area of expertise you are hiring a consultant for. The more knowledge you have will help you identify specific problems that are beyond your current level, as well as interview and make sane decisions when you hire a consultant.

How to actually hire the good consultants is another essay ;)

You hire a consultant as a teacher.

If your hope is to hire an expensive consultant to “do their thing and leave” they will do that, and it could be just fine. Or…

Look at your world-class consulting engagement as an opportunity to receive a condensed practical education from somebody in the top of their field in the context of your business.

This approach can multiply your investment in hiring experts so much.

When you are paying a consultant $25-50k a week (sounds crazy, right?) and they are doing their work, you should be taking notes and asking questions.

Have you ever seen the show Kitchen Nightmares? You’ll notice one type of restaurant owner that always bickers with Gordon “fucking” Ramsay. You’ve got a world class chef and restauranteur standing in your kitchen giving you tips and techniques to lift your business and better serve your customer… and you’re gonna bicker with him?

If you own a restaurant and Chef Ramsay is in your kitchen, you should be standing at attention, notepad poised, asking questions, and scribbling down the answers to study that night to ask more questions.

Don’t annoy the Chef, but learn, observe, and communicate where you want clarity. Take notes. Summarize in written documents. Ask questions async (email). Ask the expert to review your summaries. Use this time to your advantage.

If you consider your consultant as a prized guest lecturer there to demonstrate and teach you how to improve your business, they are money well spent.

Building a team

If things go as planned, you can start hiring experts to work with you full time. That can be an amazing experience, though you’ve also added “HR” to the list of skills you need to know and understand. You might fuck that up a few times too, as you learn about hiring and working with humans the hard way through trial and error.

Consultants are often hired to fix problems caused by poor early choices, which is fine and sometimes required. Consultants can also be hired to start new directions and ideas correctly.

Once you’ve got a team in place, hiring consultants can continue to help educate the entire team, while filling in gaps with their expert knowledge. Now the team can be present and observe the work that occurs to draw upon and fill in their knowledge gaps. The team can summarize and discuss the work the consultant is doing, as well as participate in the feedback cycle and clarification.

Hiring a good consultant can uplift your business’s product and team and work as across the board multiplier to Platform of Audience Pain Relief. The team wins, the customers win, the consultant wins, and ultimately you win too.

What if it goes bad?

It’s not all roses. Engagements can go wrong. You might end up feeling a little sad or disappointed if your expectations don’t reflect the outcomes. At that point, it’s time for some reflection and understanding. Was it a total loss? Are the gains not apparent yet? Were we attacking the wrong problems at the wrong time?

It’s a learning experience. Don’t bet the farm on a consulting engagement! It’s an intermediate to advanced technique, which in itself is on the (ever-growing) list of skills you probably need to have.

If you learn something, the odds are that a bad consulting engagement is a low-key win you and the business.

Keep going.

]]>2017-01-02T11:59:00-08:00http://joelhooks.com/blog/2017/01/02/2016-was-amazing-best-year-yet2016 as a year took a lot of criticism as a year. Beloved celebrities died in a seemingly endless parade of “noooooooo”. We were subjected to a painful and divisive US federal election cycle, which resulted in another shameless ass-clown getting elected to the highest office in the land. The entire internet appears to be on fire with rage and hate.

Shit is tough all over. 💩

If you look closely enough you might notice that mankind is doing better than we’ve ever done. We’ve definitely got a TMI problem, and our collective brains are still learning how to deal with that. It will likely get worse before things improve though as the radical advances in global communication via the internet is the single biggest thing to collectively happen to us… ever.

For me, personally, 2016 was amazing. Filled with new experiences and personal bests. There were a few rough patches too, everything didn’t go as expected. Overall though, amazing…

Big changes…

For 36 years I lived in North Texas. That’s plenty of time to soak up everything that North Texas has to offer. After a 15 year sales-pitch my lovely partner Kristina finally relented and we bought a house in Southwest Washington (Vancouver). Now my home office has this view:

Not bad! 😉

Moving is hard af. Logistically, we are a family of 6 people. We lived in our house for well over a decade. There is a sheer volume of stuff that accumulates over that time that is staggering. We had a feeling for a year or so that we’d be making the move, so we worked hard tossing all of our crap into the donation bin and trying to get the house ready to go. It wasn’t enough, and we still had a 2/3rds of a full size tractor-trailer filled with our stuff.

But, the move was a freight train. We bought a house, had extensive renovations done, and there was no turning back. It was an experience that I wouldn’t be excited to do again. The result has been fantastic and the family is adjusting to the new location. We had an amazing summer with constant visitors filling our house with family and friends.

While we lost the day to day interaction with our Texas peeps, having visitors is awesome because we get to have a full-on all-day everyday cohabitation experience. In some ways that offers more closeness than regional proximity offers.

It’s been a real treat to be back in the Pacific Northwest with my family. I was born up here, and most of family is in the area, so the move has allowed us to get some solid hang time. It’s cool to get to know them better and have our kids immersed in that side of the family.

For Kristina it has been much more difficult. Out of all of us she has made the biggest sacrifice. Her Texas roots run very deep. Everybody has had to adjust to the changes, but her reality was completely shattered, moved across the country, and glued back together. Learning how to deal with her family now being thousands of miles away. New house. In-laws. And to top it off we’ve got a baby due in February 2017 😳

They say you shouldn’t make big life decisions after a big move like this, but we said, “fuck it” and decided to add another member to our family. So almost immediately after we got moved in, after a 10 year break from infants, we are embarking on a new chapter with our 5th child. lol

You only live once y’all!

New Tattoo…

I broke the short sleeve barrier and got a new tattoo from Joby Cummings in Dallas the day before we left town.

Lost a homie…

One of my oldest and best friends passed away this year. Pete Davidson was like a brother to me. In my teen years, I had a hard time, and he was always there. We saw the Beastie Boys together on the Licensed to Ill tour, and were constant companions through high school.

Over the last few years work has afforded me the opportunity to travel a bit, and a frequent destination was Denver, where Pete loved living for the past two decades. It was really great to have the opportunity to reconnect with my good friend, and his sudden loss was a huge blow.

Friends, good friends, the kind that let you just be yourself, are few and far between and should be cherished.

In June Pete organized a trip to Jamaica with a few friends. It was a lot of fun, and something that I’ll always have to remember him by.

Love you brother, will miss you always.

So much fun…

Portland gets all of the live music and we went to many many shows. Kristina has stuck with me with out unborn child coming along for the ride. It’s been rad.

We played a lot of games, visited the ocean a bunch of times, hiked dozens of trails, went to NYC and saw Hamilton, ate endless bowls of ramen noodles, and generally had an amazing family adventure all year long. ❤️

Taking care of business…

Professionally 2016 was awesome. There were a couple of rough patches, but overall egghead.io keeps growing and improving in an incremental way and I couldn’t be more pleased with how it is shaping up.

Work is important. I love to work, but I’ve also spent most of my adult life under-achieving in a cycle of boredom and resulting depression. This business that we’ve built has allowed me to completely alter how I approach working in a radical way. I work when I want. I work on what I want. I have the opportunity to make consistent and constant improvements to a thing that helps other people achieve their goals and improve their lives.

It’s wonderful, and something that I take intense pride in.

The biggest challenges have been related to building the team behind the business. When it’s just you working on the thing with maybe the occasional contractor things are straight forward and “easy” to navigate. That’s fine, and maybe enough, but I’m nothing if not ambitious, and I’ve got a grand vision that would take me 200 years to build without help.

We are building a world class platform for smart people to deliver curated training content via bite-sized videos.

That requires a team of smart people to make it happen.

To make it challenging we also want to build an entirely remote workforce, where the team can work, play, and live wherever they want.

In the past year we’ve added an amazing illustrator, a wonderful human to interact more directly with instructors, and an awesome developer. We’ve also hired two of our part-time “interns” as full-time employees to fill out our content excellence team and work on site features for our users. As of Jan 1st, 2017 we have 9 amazing people working on egghead.io full-time.

It blows my freaking mind to see it everyday as people discuss what they are working on in our chat room. Like “holy shit, this thing is getting built!”

In 2017 we are going to double-down on a research driven approach to user experience. Accelerating development of both site features for instructors and users, as well as our content offerings.

I’ve found that I get a lot of personal fulfillment helping other people achieve success in their work lives. The way we work is fundamentally broken, and there are opportunities to break out of the shackles of the 9-5 grind. That is a big motivator for me with egghead.io as we facilitate radical lifestyle changes for many of our instructors. In December 2016 we paid out over $100,000 in direct royalties to our instructors. I’m beaming with pride here, as that is an amazing accomplishment.

I want to help people. I want to provide opportunity for other humans. I want to share what I know and facilitate success for as many people as possible.

2017 is going to be more of this. More fun, more ramen, more travel, a new baby, helping people, sharing knowledge, more hugs, more games, more tattoos, new friends, time with old friends, family, visitors… ❤️

]]>2016-04-08T14:35:00-07:00http://joelhooks.com/blog/2016/04/08/setting-goals-for-my-version-of-successThere is this amazing conference in Las Vegas once a year called MicroConf. It’s a gathering of like-minded entrepreneurs that (for the most part) are boot-strapping their business. This means that they aren’t taking outside investment, specifically of the venture capital variety. It’s a spectacular event, with interesting talks, fun parties, and amazing conversations.

At one of the talks this year, the audience was told something to the effect of “If you aren’t meeting this metric, your business is on fire and you should probably quit.”

The question and answer section of the talk was filled with panicked questions from business owners who were in the “lol, fail” spectrum of the metric.

The talk has made me consider the factors contributing to the idea of what a success is. It’s obvious that success is different for each person and/or business. What does success mean for me?

What metric determines success?

There are many paths to success, and for me that path doesn’t involve the typical trappings one might envision as required to be successful. Yachts, cars, mansions, a private island, a private party with Kanye… none of these hold my interest. For me, success means that I look forward to my day, and don’t dread tomorrow. Success is being able to spend weeks doing absolutely nothing, and not feeling stressed or guilty about it. My success is directly related to the amount of free time that I have to spend with my family and friends.

I love nice things! Don’t get me wrong, but the primary success indicator for me, however, is not maximizing revenue. My success metric is maximizing free time. The ability to do whatever the fuck I want.

These two factors overlap quite a bit. I get a big kick out of running a business. It’s actually quite a bit of fun, but as it happens, there is a lot of revenue potential “left on the table” because I just don’t feel like doing the activities that are required to make more money.

It would dig into my free time, or I don’t want to be an expert in a particular subject area. Maybe it is something that I consider boring and don’t want to have anything to do with.

It’s hard to express how much I appreciate the privilege that I enjoy to live the life I choose. Not everybody does.

Setting professional goals.

If “all the money” isn’t the goal, what is?

To use my success and privilege to help other people gain the freedom and joy that comes from maximizing free time.

To build a team of awesome humans that get to work on awesome projects and accomplish meaningful work.

To help others come to realize that work isn’t who we are, but it can be an interesting and engaging part of how we spend our time.

For most of my life I’ve defined my goals with respect to specific dollar amounts. That is more tangible, more concrete. Monetary goals make sense, and provide a very clear metric that you either hit or don’t.

For me, those goals have shifted away from specific dollar amounts, and into experiences and relationships I’d like to build. Into looking at money as a vehicle for facilitating free time. Time to relax and enjoy the brief glimmer of existence that I’ve been granted on Earth, and help others do the same.

I’m looking forward to working towards these new goals. It’s exciting, and fills me with joy.

The image gallery we will build is a simple application that displays an array of image URLs loaded from a service (Flickr), and allows the user to select them individually.

It will be built with React, using Redux and redux-saga. React is being used as the core framework to take advantage of its virtual-dom implementation. Redux will handle the management of state within the application. Finally, we will use redux-saga to handle the complexity of asynchronous sequences.

We will write the gallery in ES6 (arrow functions, modules, and template strings!), so we will need to do a bit of project setup to get going.

Project Setup and Automation

There is a significant array of options when it comes to getting started with a React application. For this simple app, we want to keep it as minimal as possible. We are going to use Babel to transpile ES6 into good ol’ ES5 for the browser, budo/browserify to serve it locally, and tape to test.

Create a file called package.json in a new project folder and add the following contents to it:

With the package.json in place, you can run npm install in the project folder and install all of the dependencies that we will need.

We’re also going to need to configure Babel with a .babelrc file in the project folder that contains the Babel presets that we want to use:

.babelrc

123

{"presets":["es2015","react","stage-2"]}

This file tells babel that we will be using ES2015 (ES6), React, and stage-2 features of the emerging ECMAScript standard (ES2016).

The package.json has two standard scripts configured called start and test. For right now, we want to get start working so we can load the application. The start script is currently configured to look inside of a folder called src so create a folder called src in the project directory and add the following files:

The index.html loads the styles.css to give us some basic styling/layout. It also loads the script build.js, which is a generated file. Our main.js is a very basic React application that renders an h1 into the #root element inside of index.html. With these files in place, you should now be able to run npm start in the project folder and navigate to http://10.11.12.1:9966/ and see the following:

Now we will build the base Gallery React component.

Displaying some images in the gallery.

First thing’s first, and we want to get some images displayed as quickly as possible! We will create a Gallery component that will display our images. In the project folder, create a file called Gallery.js with the following contents.

We’ve hard coded an array of data into this component, which is a great way to start working quickly. The Gallery extends Component, and in its constructor we set the initial state of the component. Finally, we render a basic structure with some styled markup. The image-scroller element uses the images array to produce multiple elements using map.

For now, we are using the hard-coded image URLs (via the flickrImages array), and displaying the first image url as the selectedImage. We’re accessing these properties by setting a default initial state within the Gallery component class constructor.

We can add interactivity to the gallery with an event handler that calls the setState method on the Gallery component:

By adding handleThumbClick to the Gallery component class, we can access it in any elements onClick method. Note that we are using bind(this,image) in the onClick. By passing image as the second argument, it is sent as the first argument to handleThumbClick. This use of bind is an extermely handy way to pass context to an event handler.

Looking good! Now we have some interaction, and something that resembles an “app”. Now that we’ve dealt with getting the application running and displaying data, we can consider loading some remote data. The most obvious place to do that is one of the React component lifecycle methods. We will use componentDidMount and make a call to the Flickr API and load some images:

We’ve added a new method to the Gallery class. We are using React’s componentDidMount lifecycle method to trigger the loading of data from Flickr. Lifecycle methods are called by React at specific times in a component’s lifecycle. In this case, the method will be called whenever the component is added to the DOM. Note that the Gallery component is only added to the DOM once, so this will give us our initial load of images. For a more dynamic component that is loaded and unloaded over an application’s lifecycle, this might cause excessive service calls or other unforeseen results.

We are using the fetch browser API to make a request to Flickr. Fetch returns a promise that resolves with response object. Calling response.json() gives us another promise, which is the actual JSON result we are looking for. We’ll map over the photos to create an array of Flickr image urls.

Let’s be honest. This application is simple. We could stop right here and we’d have the basic requirements done. Maybe we add an error handler in the fetch promise chain, some logic to see if there are images and DONE! At this point you really have to stop and use your imagination a bit. Simple requirements rarely last in the real world. Soon the application will grow as feature requests roll in. Authentication, a slide show, the ability to load different galleries and images sets… This is not good enough.

So now that we have a working image gallery using React, we can start thinking about the foundation of patterns that we want to use to grow the application over time. The first order of business is to take away the Gallery component’s power to control the state of the application.

We are going to introduce Redux to manage state instead, so let’s get that setup.

Using Redux to manage state

Anytime you use setState in your application you’ve allowed the Component to become stateful. While this isn’t always bad, it can lead to some confusing application code over time, with state management spread from top to bottom in your application.

The Flux architecture is one solution that was introduced to help alleviate this. Flux moves logic and state into Stores. Stores are updated when Actions are dispatched in the application. Updates to Stores will trigger Views to be rendered with the new state.

Bootstrapping Redux

The first thing we need to do is get Redux bootstrapped and running in our application. We don’t have to install anything, that was all taken care of when we ran npm install, but we do need to import and configure Redux.

The reducer function is the brain of Redux. When an action is dispatched by the application, the reducer receives the action and creates the piece of application state that the reducer owns. Since reducers are pure functions, they can be composed together to create the complete state of the application. Let’s create a simple reducer in the src folder:

reducer.js

state – this is the data that represents the state of the application. The reducer function will use this state to construct the new state. If no state has changed as result of the action, the reducer will simply return the state input.

action – the event that has triggered the reducer. Actions are dispatched by the store, and handled by reducers. The action is required to have a type property that the reducer uses to apply changes to the new application state.

For right now, the images reducer will log to the console to prove that it is connected and ready for work. To use the reducer we need to configure redux in main.js:

We are going to import the createStore function from the Redux library. createStore is used to create the Redux store. For the most part, we don’t interact directly with the store, it is something that Redux manages for us behind the scenes.

We also need to import the reducer function that we’ve just created so that it can be delivered to the store.

We will use createStore(reducer) to configure the store with our application’s reducer. This example only has a single reducer, but createStore can take multiple reducers arguments. More on that a bit later!

Finally we import the higher-order Provider component from react-Redux. This will wrap our Gallery so that we can make easy use of Redux. We need to pass the store we just created to the Provider so that it can use it for us. You could use Redux without Provider, and in fact, React isn’t required to use Redux at all! That’s wonderful, but we are going to use Provider because it is very convenient.

If you open your developer tools console, you should see a message!

It may be a bit mysterious, but shows off an interesting point of Redux. All reducers receive all actions that are dispatched in the application. In this case we are seeing an action that Redux itself dispatches.

Connecting the gallery component

With Redux, we will use the concept of “connected” and “un-connected” components. A connected component is wired into the store, and coordinates and controls action events and the store. Usually a connected component will have children that are “pure components” that take data as input, and render when that data is updated. These children are unconnected components.

note: While React and Redux play very well together, React is not required for Redux. You can use Redux without React!

react-redux provides a convenient wrapper for React components that does most of the heavy lifting required to connect a React component to a Redux store. We will add that to Gallery and make it our primary connected component:

Importing the connect function from react-redux lets us export Gallery by wrapping it in the connect component. Notice that connect()(Gallery) puts Gallery in a second set of parentheses. This is because connect() returns a function that expects a React component as an argument. The call to connect() configures that function. Soon we’ll pass in arguments to connect to configure it for our applications specific actions and state structure.

We are also exporting the connect call as the default for this module. This is important! Now, when we import Gallery it will import the connected component and not the basic class.

If you look at the console log that we’ve added to the constructor, you will see that the Gallery component’s properties now include a dispatch function. This is part of what connect has modified for us, and gives us the ability to dispatch our own action objects to the applications reducers.

We can test it out by calling dispatch in the constructor. You should see a log statement from our reducer in your developer console. We’ve dispatched our first action! Actions are plain old javascript objects that have a required type property. They can have any number of other properties as well to pass along to the reducer, but the type property allows reducers to listen for specific actions that they are interested in.

Generally reducers use a switch block to filter messages they are interested in. The switch uses the action’s type, and the reducer does its work when it gets an action that matches one of the cases of the switch.

Our application is now wired to receive actions. Now we need to connect it to the state provided by the Redux store.

Default application state

Because we are using connect, we don’t need to interact with the Redux store directly. We are going to configure a default application state to provide the Redux store.

We’ve created a defaultState object that has an empty array as its images property. We will set the state to default in the images function parameters. Now, if we log out the state property in our test case, you’ll see that it isn’t undefined! The reducer needs to return the current state of the application. This is important! Right now, we aren’t making any changes, so we can just return the state. Note that we added a default case. The reducer should always return an object representing the state.

In the Gallery we can also connect the applications state by mapping it to properties:

We are going to remove all of the image loading and interaction in the connected component for now. If you look towards the bottom of Gallery you will notice that we created a function called mapStateToProps that takes a state argument and returns an object that puts state.images into a property called images. mapStateToProps is then passed as an argument to connect.

As the name suggests mapStateToProps is a function that takes the current state, and assigns it to properties of the component. If you console.log(props) in the constructor, you will see that we now have access to the images array that we set as the default state in our reducer!

If you update the images array in the defaultState you should see some images reappear in the gallery! Now we need to get image selection wired back up with an action that is dispatched when the user clicks a thumbnail.

Updating the state

So how do we update the state with a new selected image?

We’re going to configure the reducer to listen for an IMAGE_SELECTED action, and update the state with the action’s payload.

Now the reducer is ready to receive the IMAGE_SELECTED action should it be dispatched! Inside of the case, we are returning a new state object by “spreading” the existing state and overwriting the selectedImage property. Checkout more on the ...state object spread technique in this video. It’s excellent.

In the Gallery, we will use the dispatch function in the component props by calling it inside of the body of the onClick handler function. For now we are just writing it inline for convenience, but once we make that change, we can now click a thumbnail, and it will update the selected image via the reducer!

Using dispatch can be convenient way to quickly create generic actions, but soon we will want to make reusable actions that are well named. To do this, we will make use of “action creators”.

Action Creators

Action creators are functions that return configured action objects. We will add our first action creator to an new file called actions.js.

actions.js

This could now be imported directly into any file that needed to create a selectImage action! selectImage is a pure function that only returns data. It takes an image as an argument, and adds that to the action object it creates and returns.

note: We are returning a plain JavaScript object, but the second property image might be weird if you haven’t encountered this style before. Basically, in ES6, if you pass a property to an object like this it expands to image: 'whatever value was held by image' in the resulting object. Super handy.

We’ve added a mapActionCreatorsToProps function that takes the dispatch function as an argument. It returns the result of a call to bindActionCreators with our GalleryActions provided as an argument. Now if you log the props, you’ll see that Galleryno longer gets passed the dispatch function, and instead has a function called selectImage that we can use directly!

To review, we’ve done several things:

created a reducer that contains the initial (default) state of our application and listens for actions

created a store that consumes the reducer and provides a dispatcher that we can use to dispatch actions

connected our Gallery component to the store

mapped the store’s state to props that are passed to the Gallery

mapped an action creator function so that the Gallery can simply call selectImage(image) and the application state will update.

How do we use these patterns and load data from the remote data source?

This is where it gets interesting!

Asyncronous activity?

You may hear the term “side effects” used when discussing a functional style of programming. Side effects are things that occur outside the boundaries of the application. Within our cozy bubble, side effects aren’t really a problem, but when we reach out to a remote service the bubble is pierced. We lose some control, and we have to accept that fact.

In Redux, reducers don’t have side effects. This means that reducers don’t handle async activity in our application. We can’t use them to load our remote data because reducers are pure functions with no side effects.

Redux is wonderful, and if you don’t have and side-effects like asynchronous activity you could stop right here. If you’re creating more than the most trivial example, it is likely that you are loading data from a service, and this is of course async.

note: one of the coolest aspects of Redux is how tiny it is. It does so very little! It is intended to solve a very limited problem scope. Most applications will need to solve lots of problems! Luckily Redux provides the concept of “middleware”, which are basically bits of code that sit in the midel of the action –> reducer –> store triangle and provide a mechanism for introducing side effects like async calls to remote servers

One approach is to use thunks with the redux-thunk middleware. Thunks work great, but can get confusing for sequences of actions and can be challenging to test effectively.

select an initial image for display when the results have been received

handle any errors that might occur

This is all before the user has clicked anything in the application!

So how do we do it?

This is where redux-saga can be of great service to our application!

redux-saga

redux-saga is built to handle asynchronous actions in our Redux applications. It provides middleware and a handful of effects methods that make building complex sequences of asynchronous actions a breeze.

A saga is a generator function. Generators are an ES2015 addition to JavaScript. This might be your first encounter with generator functions, and if that’s the case, they might be a little weird. If that is the case, take a few minutes to read ES6 Generators in Depth and watch this short generators video. Don’t fret to much if you’re still scratching your head. To use redux-saga you won’t need a PhD in JavaScript Async Programming. Promise.

Because of the way generators work, we are able to create flat sequences of commands describing complex workflows within our application. The entire image loading sequence described above could look like this:

No matter how long you stare at the console, your “hello” will never arrive

This is because sayHello is a generator! Generators don’t execute immediately. If you changed the line to sayHello().next(); your greating will appear. Don’t worry, we won’t call next all the time. Like Redux, redux-saga is built to remove pain and bolierplate and make our development experience more pleasurable.

We’ve imported the applyMiddleware function from Redux, and the createSagaMiddleware from redux-saga. When we create the store, we need to supply Redux with the middleware that we want to use. In this case we call applyMiddleware and send it the result of createSagaMiddleware(sayHello). Behind the scenes redux-saga loads in the sayHello function, and politely calls the initial next for us.

It should greet you in the console!

Now let’s build a saga for loading images.

Loading Images with a Saga

We’ill get rid of the sayHello saga and replace it with a loadImages saga in sagas.js.

The fetchImages method returns a promise. We are going to call fetchImages, but we are going to use the yield keyword. By dark arts and sorcery, generators understand promises, and as the console log will show, we’ve yielded an array of image urls. Looking at loadImages, it looks like typical syncronous code. The yield keyword is the secret sauce that lets us code in this syncronous style for asyncronous activity.

Encapsulating our async API requests

Let’s define the api we want to use in its own file. It is nothing special. In fact, it’s the same code we used earlier to load Flickr images. We’re going to create a file called flickr.js in the src folder:

This isn’t strictly required, but it makes a lot of sense to me. We are at the very boundaries of our application, where things are a bit messy. By encapsulating the mechanics of the interaction with the remote API, our code will be cleaner and easier to update. It also makes it dead simple to completely swap out the image service provider! Nice.

We still need to get data out of our saga and into the application state. To handle this, we will utilize “effects” provided by redux-saga.

Update the application from a saga

We could probably call our saga with the dispatch function or store as an argument, but that approach would be unpleasant and perhaps a tad confusing over time. Instead, we’ll rely on a method provided by redux-saga called put.

First we will update reducer.js to handle a new action type IMAGES_LOADED

After importing put, we add another line to loadImages. It yields the result of the call the put which sends along an action object. Behind the scenes redux-saga dispatches that for us, and the reducer receives the message!

What if we don’t want to loadImages implicitly like this, simply because we have wired up a saga? How do we trigger a saga with a specific type of action?

Triggering saga workflows with actions

Sagas become much more useful if we have the ability to trigger their workflows with Redux actions. When we do this, we can leverage the power of sagas from any component in our app. First we will create a new saga called watchForLoadImages.

This new saga uses a while loop so that it is always active and ready. Inside of the loop we are yielding a call to a new method from redux-saga called take. Take listens for actions of a given type, and when they occur, it advances the saga to the next yield. In this case, we are yielding a call to loadImages, which initiates the loading of images.

Blocking and non-blocking effects

This works fine for our application, but there is a broader issue that we should be concerned with. The watchForLoadImages sage contains blocking effects. What does that mean? Well, it means that we can only execute a single LOAD_IMAGES workflow at a time! It isn’t obvious with a simple example like this, because we actually only load images once, but it is definitely a consideration. In fact, the general practice when listening for action evens is to use the fork effect instead of yield loadImages().

Using the fork helper will convert our watchForLoadImages into a non-blocking saga that can be executed regardless of whether or not a previous call is in progress. redux-saga provides two helpers, takeEvery and takeLatest that assist in these situations.

Selecting the default image

Sagas are sequences of actions, so we can add more aspects to a saga easily.

As part of the loadImages workflow, we can yield another call to put with the IMAGE_SELECTED action type and send along the image we want to select when images are loaded.

Handling errors

If something goes wrong inside of the saga, we might want to notify the application so that it can respond accordingly. Do do this, we simply wrap the workflow in a try/catch block, and yield a put with a nitification that has the error as the payload.

Testing Sagas

Using Redux makes testing most of our app a breeze. Check out this egghead course for lots of techniques for testing React in general.

One of the awesome aspects of redux-saga is how easy it makes testing these bits of asynchronous code. Testing async javascript can be a real chore. With sagas, we don’t need to jump through hoops to test this core functionality of our application. Sagas take the pain out of aync tests! Which means we will write more tests. Right?

We’ll go ahead and import everything we need for now, and add a single test. The test function takes a name and a function as arguments. Inside of the function, we create an instance of the sage generator. Armed with that instance, we can start advancing the saga to test each step.

The assert.deepEqual method takes two values and checks to see if they are equal (deeply!). The first is a call generator.next().value which advances the generator and gets the value. The next value is simple false. We want to see it fail! The final argument is a description of the expected behavior.

The test fails as expected and the results are interesting. The actual result is { TAKE: 'LOAD_IMAGES' }, which is the output we receive when we call take('LOAD_IMAGES');. In fact, our saga could yield an object instead of calling take, but take gives us a little sugar and eliminates annoying keystrokes.

Hmm, { _invoke: [Function: invoke] } is definitely not as obvious as the simple object that we got when yeilding take.

This is a problem. Luckily it’s one that redux-saga has solved in a nice way with effects like fork. take, fork, and other effect methods return and easily testable simple object. The object is a set of instructions for redux-saga to execute. This is beautiful for testing because we don’t have to worry about the actual side effects (like remote service calls). All we care about are the commands we are requesting to be executed.

Instead of a function invocation, we get a plain object. That object has the loadImages function embedded in it. The application loads exactly the same in the browser, but now we can easily test this step in the saga workflow.

Testing the loadImages saga is similar. We need to update yield fetchImages to yield fork(fetchImages).

123456789101112131415161718192021222324252627282930313233

test('loadImages',assert=>{constgen=loadImages();assert.deepEqual(gen.next().value,call(fetchImages),'loadImages should call the fetchImages api');constimages=[0];assert.deepEqual(gen.next(images).value,put({type:'IMAGES_LOADED',images}),'loadImages should dispatch an IMAGES_LOADED action with the images');assert.deepEqual(gen.next(images).value,put({type:'IMAGE_SELECTED',image:images[0]}),'loadImages should dispatch an IMAGE_SELECTED action with the first image');consterror='error';assert.deepEqual(gen.throw(error).value,put({type:'IMAGE_LOAD_FAILURE',error}),'loadImages should dispatch an IMAGE_LOAD_FAILURE if an error is thrown');assert.end();});

Take note of the last assert. It’s testing the catch by using throw instead of next on the generator. Another cool feature is the ability to send values in. Notice that we’ve created an images constant, and we pass that into next. The saga uses the value we pass in for the next step in the sequence.

It’s awesome, and this level of control is a dream when it comes to testing async activity!

make it a slide show where the image advances to the next image on a timer

allow the user to search Flickr with keywords

drop in another API that delivers images

allow the user to select what APIs to search

We’ve only touched the surface of generators, but even at this level, hopefully you can see how useful they can be when coupled with the redux-saga library, Redux, and React.

]]>2015-12-31T00:18:00-08:00http://joelhooks.com/blog/2015/12/31/2015-year-in-reviewIt’s been two years since I woke up dreading a workday.

And I work almost every day.

When we first set out to build egghead, I told my parter John that all he had to do was make amazing content, and that I would do the rest.

For the most part, I took that very seriously, and wore all of the hats for a long time.

Systems engineering

Front end development

Back end development

Quality Assurance

Developer Operations (DevOps)

Customer Support

Marketing

Search Engine Optimization

Copy Writer

Enterprise Sales

Talent Scout

Evangelist

So many hats.

When you bootstrap a business, meaning you build a business from the profits of the business, and not with debt or outside investment, it creates an interesting situation. How do you get all of those things done, if you don’t have the money to pay somebody to do them?

The answer is, do them yourself. The reason I learned how to write software was so that I could apply it to my own business.

But wearing all those hats eventually gets tiring, believe it or not ;)

There are two hats I’ve never worn with this business, and that is bookkeeper and accountant. From day one I’ve had somebody that has taken care of that, and our compliance is spot on and the source of exactly 0 stress.

Outside of the books, 2015 has been the year of slowly relinquishing control by bringing in awesome people to help.

First and foremost, is my partner John. Sure, our deal was that he’d be in charge of making content. And that still the case, except he’s in charge of making sure the content is amazing for the 33 instructors we’ve added to the roster since egghead became a business.

We’ve paid out almost $500,000 in royalties to date.

I’m so proud of what John and I have built, and out of all the metrics (and I track EVERYTHING), that is the one that fills me with the most joy.

The inbox fills me with pain.

So, a confession, I’m not very good at a lot of the jobs in the list above. In particular, I make a horrible customer service representative. I’m short tempered. I’m a smartass. I’ve got a low tolerance for repetition. My brevity can come across as rude… the list goes on.

And my inbox is the primary target for all incoming communication from thousands of paying customers and tens of thousands of potential customers.

It was crushing my soul.

Until Gina, this amazing woman who seems to genuinely love interacting with people and helping to make them happy. Who has sympathy for their problems, and works to make it right.

Who filters my inbox into a tidy list of things that I need to take care of.

Inbox 0. Twice a day.

It was a game changer, and lifted a massive weight from my shoulders.

We also started using intercom.io heavily, and that has been wonderful as well as a way to take the conversation out of the inbox, and into something that is simply easier to manage and more pleasant for everybody involved.

Working directly with those that inspire

As egghead has steadily grown, we’ve seen more revenue over time. In 2015 this reached the point that we could hire some amazingly brilliant consultants to help us push the accelerator down a bit further.

Brennan Dunn is a friend and mentor, that was integral in the origin story of egghead. His books and classes inspired me, and directly led to the tipping point where I said “fuck this” and started my own business.

For the first 6 months of 2015, I had the pleasure of working with Brennan on marketing egghead.io, and it was awesome. Brennan helped to double our email list size, and taught me some killer techniques that I will use for years to come.

Hunting Unicorns

This summer we found ourselves in an interesting position. We could hire somebody full-time to work with me developing the egghead platform.

Leonard Souza started working with us on July 1st, 2015. About 10 days later, I basically went on vacation for 3 months. hah

Leonard held it together and started shipping amazing features for our users without a lot of direct interaction with me.

That is wonderful and amazing.

Cherry on top

Towards the end of the year, we engaged Nick Disabato to help us with some targeted testing and revisions for the egghead.io website. That has gone extremely well, and we’ve doubled down and brought nickd on for a more in depth longer term engagement.

I’m so excited about this, because nickd is fantastic to work with, and the project is exciting and fun.

Instead of waking up dreading my workday, I wake up pinching myself, exciting to go to work with an amazing team filled with people that challenge me and are excited to work on this business that we’ve built from the ground up.

Building a community

One of the most interesting outcomes of building egghead has been the community. We invite our instructors and potential instructors into a Slack chat room. We do this so we can discuss lessons and courses, and mentor people that haven’t published a lesson yet get a lesson published.

The result of this is an amazing chat filled with smart and inspiring people from around the world.

It’s precious to me, and like many aspects of this adventure, fills me with amazement that it even exists!

What’s next in 2016

Publish lessons from our first women instructors.

It is frustrating. We are working on it. The ratio of women to men in technology is already embarrassingly low. Add “screen casting” as a requirement to that, and the barrier to entry gets very tall.

It will happen! Soon.

Expand past JavaScript.

We love JS! It’s been tricky, growing the content that we offer without alienating the core base of customers. It has been a goal since the beginning, but this year you will see egghead lessons on Elm, ClojureScript, CSS, and perhaps other interesting emerging technologies. Fun times.

Double down (again) investing in smart people.

We want to evolve our platform into something amazing that empowers people across the globe to learn new skills and achieve a better life for themselves and their families. We want to continue delivering content in an aggressively free manner, and carve learning paths through the confusing jungle that is programming computers.

Beyond work

Well, one thing I didn’t do in 2015 was blog. The last post here was my year in review for 2014. I don’t know if that will change much in 2016, we will see. I actually have a lot of things to say about building a business, running a business, online learning, programming computers, raising kids… I just keep it to myself.

We had some amazing travel. We road tripped to Portland through Zion National Park (amazing). Unfortunately on the first morning in our Portland rental house I slipped on the stairs and slammed down into my ribs. Broken ribs take a long, long time to heal. They still hurt after 7 months. Needless to say, it put a damper on the trip. I limped through it and powered through some light hiking.

In July, I spent the week in the mountains of Colorado near Aspen creating art on computers and using LASERS with Joshua Davis. Seriously, fuck ya. It was awesome. I brought my oldest kid (17) to the class, and we had a great time making art for a week. Definitely going back again in 2016.

In August we set out on our longest family trip to date spending a full month on Lake Champlain in Vermont. It was amazing. Hiking, boating, laying about… A really wonderful time.

My friend Jon tricked me into slaughtering and butchering a pig for a whole hog dinner.

It was quite the experience. As we are using a knife to scrape the fur off the pig’s skin, we ask his brother the hog farmer, “How do you usually do this?”

“I’ve never done that. I just pay a guy $40.”

Fuck you Jon. I love you, lol.

Kristina and I spent a week in London, that was great.

Eating well

Last year I was super proud to report that I lost 50 pounds. I found 25 of it! hah. I’d like to blame it on the rib injury, but mostly candy is delicious.

Cigarettes smoked

0

0mg of nicotine since October 2014.

It’s wonderful.

Professional Skills

I spent the year working with React and learning RxJS. I focused on functional programming, and the last two months of the year I’ve been shouldering into Clojure. It’s been great, and I feel like I’m finally making progress. This is going to continue to be my goal for 2016. I’d like to get good at Clojure, and deploy my first real piece of software (for egghead) built on Clojure!

A nice side effect of wearing all the hats. I get to make zany tech decisions based on what I think is cool at the time! ;)

Move

2016 is going to be the year we leave Texas. It’s been a long time coming and I’m stoked that the wheels are in motion. 35 years in North Texas is baffling to me. I’ve wanted to leave for 23 of those!

]]>2014-12-24T11:12:00-08:00http://joelhooks.com/blog/2014/12/24/bootstrapping-egghead-dot-io-to-feed-my-family-2014-in-review2014 Year in Review

It’s that time of the year. People dusting off the blog and endeavoring to capture the past 12 months of their life in a wordy nutshell. It’s something that I’ve never participated in, but I thought I’d give it a try.

I quit my job.

This still frightens me. In February I quit a job that paid very well and provided a steady stream of work. This goes against the very core of my being, but it had to happen. It was killing my focus on what I truly wanted to do.

Building egghead.io

In late 2013 I partnered up with my friend John Lindquist to convert his egghead.io website from a list of YouTube videos into a subscription service providing short topical video lessons to web developers several times per week.

One of my first orders of business was to introduce additional content producers to teach material, as burning John out is something I plan to avoid. Initially I produced some lessons, but through the course of the year I’ve tapered that off to focus on the business side of the business.

This has turned out very well, and we’ve had many instructors contribute content throughout the year. It is surprisingly difficult to produce a 2-10 minute concise lesson in screencast form, so the instructors tend to work in bursts.

To date, we’ve paid just over $100,000 in royalties to our instructors.

This makes me so freaking proud. It’s amazing, and when I looked at the numbers and saw that, it was hard to believe.

In May, John was able to leave his (beloved) job to work on egghead fulltime as well.

“I’m leaving my dream job to work at my dream dream job,” is what he told me.

We’ve built a platform that allows smart people to teach other smart people what they know and receive non-trivial compensation for their work.

This. More of this.

Improving egghead.io

While my initial “design” for egghead was OK, it was plain-old-boostrap, and generally kind of meh. But, they call bootstrap bootstrap for a reason. We are bootstrapping a business, spending just what we have to and delivering value.

As revenues have increased, I’ve been able to slowly build a team. This has been wonderful, because building something by yourself gets very very lonely.

Our team is modular. We hire consultants, and pay them monthly retainers for blocks of work. The first was the wonderful Jane Portman to start refreshing the overall design.

Because I’m terrible at it, and hate it, we hired Evgeniy Nagalskiy to take Jane’s designs and convert them to HTML and SCSS for me. This has allowed the designs to actually see the light of day, instead of being a constant source of dread for me.

We’ve also been able to engage Pete Keen, who wrote the book on Stripe payments with Rails that saved my ass when building the initial Stripe integration. He’s been a huge help on the Rails side, considering I have no idea what I’m doing most of the time ;)

We also got some help from Matt Jaynes, who built a killer Vagrant/Ansible VM for egghead local development.

This is fun and exciting, and 2015 is going to be awesome.

Teaching AngularJS on-location

By February, egghead.io revenue was nowhere near the level to match my consulting income and feed my family. To supplement the missing income, I started doing in-person training of web developers for companies. This has been a lot of fun, keeping me engaged with lots of smart people around the country. I’ve tried to keep it at a sane level, but I’ve hit the 2nd tier of airline status for the first time ever… lots of travel!

The first few times were very tough. I’ve done workshops, but they were free conference workshops. These companies pay me a very high rate to teach their staff the ins-and-outs of AngularJS in 2 to 3 days.

It is hard work, and I’ll likely wind it down through 2015 as I focus on egghead.

Writing…

For most of the year I’ve been slowly writing a book on AngularJS Automation with Grunt. By the end of this year it will be complete, and I will start on the Gulp version.

For the most part, it serves as a source of guilt. Something I should be working on, but I’m not. I should take this as a sign?

I’ve neglected my blog and email list as well. I’ve been totally engrossed with building the egghead business, that I’ve kind of let this space fizzle.

Writing is something I enjoy, so I will get back at it. I’m in a weird middle space where I feel like I’ve got interesting things to say about bootstrapping a business over technical topics. The problem is I lack confidence, which isn’t rational, because by any measure I’ve got some experience in the area now.

Travel

I was all over the US in 2014 for the training. My favorite trips were probably to San Francisco. Love the energy and concentrated intelligence in that city.

John and I got to deliver workshops in Paris for ng-europe. This was very exciting for me, as I’ve never been to France. Paris is amazing, and I will definitely go back. The highlight was a culinary tour by the awesome Ute Biefang. If you like food and are heading to Paris, you should book a tour. It was fantastic.

This summer we went on a cross-country road trip with our 4 children. It was an epic journey to Vermont via Tennesee, NYC, and Boston.

In NYC my 7 year old daughter saw a “family” of rats in Central Park and exclaimed, “It’s a dream come true!”

One of her favorite books, A Cricket in Times Square, feature a mouse that gets his meals in Central Park. heh

This was our morning view for two weeks. Lake Champlain is awesome. We are going back next year.

Driving fast!

I spent a weekend at a High Performance Driver Education track event. It was freaking amazing. So much fear. So much fun.

It was very challenging to drive into corners at speeds your mind knows are not “safe”, pushing the car to the limits. Highly recommended to anybody, with any vehicle. I love to drive, and thought I was good at it. I learned how little I actually know, even after 25 years of driving. The weekend improved my driving skills immensely, making me more aware of both my vehicle and the road around me.

Stopped smoking for good…

I’ve had a love hate relationships with nicotine for many years. It’s done. No vaping, no gum, no sneaking, just done. If you want to quit, I highly recommend Allen Carrs Easy Way to Stop Smoking. It is a great book that uses cognitive behavior therapy in an effective way.

Fuck you cigarettes.

Lost 50 pounds…

I lost 50 pounds, which is awesome. I’ve slacked off a bit and am back up 10, but haven’t regressed fully into old habits. This is something I’d definitely like to keep up through 2015 and beyond, as it sure is nice to not have that extra weight. I used the Primal Blueprint approach, and found it to be a moderate paleo that is sustainable.

For a brief period I was lifting weights too, and enjoying it. I was using bad form and did something to my elbow. It’s hurt for many months, and I dread seeing a doctor because it will probably require cutting. Need to bite the bullet and just get it taken care of.

What’s next?

2015 is going to be awesome. I’ve got big plans for egghead, and look forward to many technical challenges alongside the business and marketing aspects that are fun too.

I plan on digging deep on reactive programming with React and RxJS while honing my functional programming skills at the same time. I’d love to have a working knowledge of Clojure.

There will be more driving in my future as well. I plan on attending several HPDE events, as well as a 3-day off-road rally course at DirtFish. The latter I am particularly excited about!

Bring it New Year.

]]>2014-02-11T15:45:00-08:00http://joelhooks.com/blog/2014/02/11/lets-make-full-ass-angularjs-directivesWith best intentions we set forth to create the mighty directive. The steepest slope of the dreaded AngularJS learning curve.

The “place where the jQuery goes.”

So what is a directive? We’ve talked about this before, and decided that they are not where the jQuery goes… usually… if you are going to use jQuery, directives are definitely where it should go.

It’s easy to say “OMG, NO JQUERY” – but what does that actually mean? What does a really kickass directive look like?

The Anatomy of a Good Directive.

The ui-bootstrap library is the best singular resource on what a good directive should look like. They are solving multiple common problems in varied ways, with generally solid patterns and practices. It’s a robust open-source project, with a constant flurry of activity.

When you start to dig through the library’s src, you can see how many different styles and solutions have solved the various problem. Some are extremely complex, while some are relatively simple.

While the solutions are varied, there are also some common traits the ui-boostrap directives share across the library.

Minimal use of the link function

How many of your project’s link functions contain all the things?

me: raises guilt hand sheepishly

Down and dirty, just toss it in the link function.

When you start to browse the ui-bootstrap code, pay attention to the link functions on the directives themselves.

It’s obviously much bulkier than the actual directive, but it’s also crystal clear what the controller is doing. Normal controller stuff!

One particular item of note is the init function. In the directive above, you probably noticed that the link function did exactly one thing:

1

dropdownCtrl.init(element);

Since you can’t get at the element in the controller, this allows us to still have access to the element, but in a clean, testable, injected way.

If we were going to critique the controller, it might be about its access to $element at all. Is the controller the right place to be doing any DOM manipulation, even if it is ever so slight? Where else would we do that if we aren’t going to do it in the link function or the controller?

The AngularJS ‘service’ is where the boundaries of our applications live.

connections to the outside world

domain/data models

core logic

…

the DOM?

This actor is wonderful. It connects all the dropdowns and manages their shared state. In this case, we can only have one open drop down on the page. dropdownService keeps track of who that is, and if another drop down is opened, it snaps the current one shut before allowing the next to open.

Additionally, the service listens for events on the document to close the open drop down if the user clicks the page or hits the esc key.

One central place to encapsulate what would otherwise be confusing spaghetti logic on line 342 of a typical directive’s link function.

Pause and study

It really pays to take pause and review a little code when you sit down to write a complex directive. For one, ui-bootstrap covers a lot of ground. The entire point of Bootstrap (proper) is to provide a robust set of typical components. Meaning, odds are the component you are building (at least the soul of it) is likely covered by the Boostrap component set.

ui-bootstrap conveniently provides this world class reference implementation of the most common web application components… the Angular Way™.

]]>2014-02-06T00:33:00-08:00http://joelhooks.com/blog/2014/02/06/stop-writing-for-loops-start-using-underscorejsHow many for loops did you write today? This week?

It seems a little weird at first, and I still hit the docs for methods like reduce above. Knowing they exist, and a flat refusal to use for loops is my primary weapon. The above methods are really just scratching the surface. The underscorejs library is filled with awesome utilities like this that can be combined together to create new and wonderful things.

The 30 day no-loop challenge

Stop.

For the next 30 days, don’t write any for loops. If you see a nasty pile of those gnarly things, replace them with an each or a map. Do a little reducing. And let me know how it goes!

Beware. Underscore is the gateway to functional programming. What has been seen, can’t be unseen. In a good way!

If you’re wanting to dig a little deeper, you should jump over to this tutorial on functional programming in javascript. It’s great and only takes about ½ hour to work through. It is “how the sausage is made” fundamentals for the underscore functions I used above. Lot’s of wholesome nerd fun!

note: As a more performant alternative to underscore, you might check out lodash

note: it should also be noted that modern browsers support the above methods natively. Array.forEach, Array.reduce, and Array.map exist, but to use them you likely need to create shims to fallback for cases when they don’t exist. For me, having the consistent underscore (lodash) API is much more convenient. YMMV

note: Yes, for loops are faster. I optimize for readability and ease of use for my team before squeezing performance out of CPUs. I don’t write games, or rich animated consumer experiences. Big projects, 10s of developers, code that already trends towards sprawling and messy.

The “clean readable code” optimization pays huge dividends, even if it comes at the cost of (very) marginal performance hits.

Now, if we are doing a big list of items in Angular, we focus on performance in terms of CPU, but even then, the only time we hit a wall with an unoptimized datagrid was on last-gen Android phones.

Clean first! ;)

]]>2013-10-30T11:19:00-07:00http://joelhooks.com/blog/2013/10/30/how-to-build-a-subscription-service-on-rails-a-noobs-guideThere are few things as nerve-wracking as pushing your first subscription
website into production. Am I covering all the bases? Will everything break and
leave me in a pit of customer support sadness? How do I even take payments? Are
they just going to laugh at me?

Some Background

My friend John makes awesome AngularJS training videos. They are short, topical, and
presented clearly. He loves making training videos, but has very little interest
in building out a website and marketing the content. “Selling things on the
internet” can be a chore. If you’re just selling one off digital things, it is
fairly straight forward, but if you are looking to build out a website, allow
users to register, and charge them on a recurring basis it can be anything but.

As it happens, I’m very interested in these problems. For the last couple of
years I’ve been taking
classes
and going to
conferences
that boil down to selling things on the internet. Recurring revenue is alluring.
My gurus are all about finding pain and providing immense value to people in
exchange for money. This is what I want to do too.

Wonder Twin powers; activate!

Digging in: The Stack

We have content, traffic, and a solid reputation. What’s the next step?

To actually build something, of course. Rails was the clear
choice
when it came time to decide what framework to use. It is mature, easy (enough)
to use, and has a metric shit-ton of resources for building this type of thing.

It is also astonishingly easy to deploy Rails apps to Heroku. This has been a
huge win. Using the free Heroku tier has allowed me to have a production and
staging environment a few keystrokes away. For production, Heroku quickly becomes not free as you add in background workers, SSL, and other essential pieces, but you can go a long way with free.

ZOMG There is so much to learn!

I’ve been developing software professionally for a few years. My roles are
primarily on the UI side of things, but I’ve built a few full-stack solutions in
the past with Django. Over the years I’ve always wanted to learn Rails, I just
never had anything real to build. Tutorials and books can be boring, especially
with no context of something “real” that you actually care about to build. That
said, there were a few excellent resources that helped me on the way:

Michael Hartl’s Rub on Rails Tutorial: Outside
of basic Ruby syntax, this is where you want to start. The web version is free,
and he guides you through building a “real” app with Rails in a clear, easy to
understand manner.

Daniel Kehoe’s RailsApp Project: This was a huge
boost for me. It covered all the bases. I wanted to build an app with Rails.
Check. I wanted to use Twitter Boostrap with Rails. Check. I wanted to have
authentication and authorization in the app. Check. I wanted to integrate
recurring subscription payments. Check.

Peter Keen’s Mastering Modern
Payments: If you are going
to sell stuff via a Rails app, buy this book today. It saved me so much
time and instilled a confidence that I might never have had without it.
RailsApp is pretty good, but for me it started to break down when I wanted to
add payment. I needed deeper understanding and control, and Pete’s book
delivered the knowledge I needed in a concise 120 page guide. I’d recommend
getting the verion with the code, because it was a huge help to me.

Giles Bowkett’s Software as a
Disservice
is a direct critique of RailsApp. It is harsh, but constructive. It addresses
code smells that I had noticed, enforced some of what I learned in Pete’s book,
and lays out some solid practices that a Rails noob like me won’t learn in 1000
searches that result in a Stack Overflow answer. It’s an opinionated style and
practices guide. note: RailsApp has its flaws, and it was extremely useful to
me. Daniel is passionate and has put out a lot of excellent material, but
charging people real money for a product or service is terrifying. I needed
this critique to plug some serious holes in the implementation. It is important
to recognize that Giles is critiquing the code and not the developer that
wrote it.

These four sources were keys to the successful launch of egghead.io’s Pro
subscription service. Along with countless posts, Stack Overflow answers, and
documentation written by the Ruby/Rails community. It is amazing. Now is a
very good time to be a nerd.

Taking Payments: *scared face*

It really is terrifying. Maybe it isn’t as scary if you’ve done it before and
know what you are doing. I didn’t qualify.

Luckily we live in a fantastic age of modern convenience.

Stripe.

What an amazing service.

No complicated merchant accounts or setup

By developers, for developers

Wonderful API

Excellent resources for testing

Solid documentation

Easy integration

They handle a huge amount of the drudgery invloved with taking payments from
people on the internet. They remove the horrors of PCI compliance by providing
an implementation that allows you to take credit cards without ever actually
having the actual credit card numbers touch your system. They travel over
secure-socket-layer directly to Stripe, who responds with a token that allows
you to initiate an authorized charge. It is beautiful. Once you’ve authorized a
subscription, Stripe manages the recurring billing for you.

To help with the subscriptions, I found the Koudoku
gem to be a great resource. While I
had to fork it and manipulate it to some extent for my specific needs, it does a
lot of the heavy lifting. I’m on the fence about using a gem to handle all of
this. On one hand, it is very easy. On the other, having your subscription
implementation tucked away in a “black box” can bite you when the shit hits the
fan. Give and take.

What would I do differently?

Testing

Testing

Testing

It is tough, as a noob, to get in and write proper tests. All of the
resources I listed above discuss testing. I skipped it. You know what is
really hard? Going back through a non-trivial system and writing a solid
test suite.

This can’t be said enough. Tests should guide design of code and when they
are tacked on later, they lose a big chunk of value. This isn’t to say they
don’t still provide immense value, but “test later” often means “test never” and
this isn’t a good situation to be in.

I’m working on retrofitting tests, but it is a chore. Test early, test often.

Summary

Building a subscription service is scary. Luckily the internet is stuffed full
of open source projects, examples, and helpful people sharing knowledge. Some of
it is free, but some of the best resources cost a few dollars. Worth every
penny.

Rails is a great framework for this sort of thing. I’d recommend it highly, if
you are trying to build an application that requires recurring billing for your
users.

Test early, test often. It’s an additional thing to learn and understand if
you are just starting out, but it is worth the effort. If I could go back and do
anything differently, it would be to write solid tests along the way. Don’t make
the same mistake with your apps!

]]>2013-09-15T11:24:00-07:00http://joelhooks.com/blog/2013/09/15/why-i-built-an-angularjs-training-site-on-railsIf you’re into AngularJS at all, you are probably familiar with the kickass AngularJS video training from egghead.io. If you haven’t seen egghead.io, it is a collection of 50+ short “bite-sized” training videos, largely focused on the AngularJS framework.

The videos are created by (and feature) my friend John Lindquist playing Webstorm like a violin while explaining basic AngularJS concepts.

Over the last few months John and I have been discussing egghead.io, and what its future holds. Is it an AngularJS training site? Is John going to be the only presenter? Is the donation model the best approach for keeping the lights on?

The Problem

egghead.io was a static site, built on AngularJS. It was using the YouTube API to grab a list of videos from a playlist, and list them out in a simple list. It had a permanent “in progress, fixing stuff” message in the top left, and a request (plea?) for donations in the top right. When you would click on of the videos, it would display the embedded video in the page.

Frankly, this was adequate for John’s needs. He just wants to create content and teach people. He didn’t want to think about:

SEO

Content pipelines

Information architecture

Server maintenance

Marketing

Set it and forget it.

As it happens, I’m really interested in all of those topics.

The Solution Part 1: Ruby on Rails

I was asked, “Why would you build an AngularJS training site with Ruby on Rails?!”

This was the first time I’ve used Rails, outside of tutorial type projects. It has been floating at the top of my “to learn” list for several years. Aside from basic nerd compulsion to explore cool technology, it became rapidly apparent that this was the right tool for the job.

One of the drawbacks of a single page app, built with AngularJS or another similar framework, is that SEO become extremely tricky. There are approaches for overcoming this, but it is beyond trivial. egghead.io is a web page with the purpose of delivering content. Web pages need to searchable, so people hunting Google will actually be able to find them. Egghead had a bit of search traffic. It actually does fairly well (2nd page) in a search for “AngularJS”. What wasn’t getting search hits was the video lessons themselves.

By rendering the pages on the server, and delivering them to the browser, Google can now crawl the pages and actually show the pages to searchers interested in the content. It didn’t take long to see a bump in organic search traffic.

I was also able to get authentication squared away with Devise and CanCan. It was a bit tricky, since I chose Rails 4 and Bootstrap 3. Many of the gems required using git branches, but after some trial and error it all dialed in very nicely.

Rails wasn’t the only choice for this. I also considered Django and a Node stack, but ultimately the “convention over configuration” nature of Rails won my heart. It is so freaking easy and pleasant to work with. After a month, it has earned a well-deserved slot in my web development toolbelt.

The Solution Part 2: Heroku

Even with a fair bit of traffic, I’m able to comfortably get away with the free plan. Through the use of strategic caching and CloudFront to serve static content, the server itself doesn’t have to do much heavy lifting.

git push heroku master

I’m not scared of managing my own VPS, but there is enough work to do without getting into SYSOP tasks. Heroku ftw.

The Solution Part 3: Wistia

TIL: video seo is a thing.

You ever notice your search results that show a preview thumbnail of a video with the little “play” icon? Usually these are for YouTube results. This is because, and is no shocker, Google is really good at video SEO. For YouTube. Try as you might, if you are hosting your videos on YouTube, you will never see these “rich snippets” on your own pages where you embed the videos.

Video SEO is black magic voodoo. It involves creating a specific sitemap XML that correlates media to a URL. It looks something like this:

12345678910111213141516

<url><loc>http://egghead.io/lessons/bower-introduction-and-setup</loc><video:video><video:content_loc>http://embed.wistia.com/deliveries/4f239fa48f86dd8854a707fa6384de5aa3c54db7/file.mp4</video:content_loc><video:thumbnail_loc>http://embed.wistia.com/deliveries/3df06507df5589c130ceb906a59d8f04f9a5f034/file.png</video:thumbnail_loc><video:title>Bower - Introduction to Bower</video:title><video:description>Bower is a package manager for Javascript libraries that allows you to define, version, and retrieve your dependencies. In this tutorial, John gives a quick introduction to Bower and will show you how to get started.</video:description><video:publication_date>2013-09-05T14:27:26+00:00</video:publication_date><video:family_friendly>yes</video:family_friendly><video:duration>168</video:duration><video:tag>bower tutorial</video:tag><video:tag>bower setup</video:tag><video:tag>bower angularjs</video:tag><video:tag>bower screencast</video:tag></video:video></url>

You can associate your YouTube videos with a video sitemap, but it is fruitless. Given the same video, Google will always favor YouTube.

Videos on the egghead.io domain are embedded via Wistia, and not YouTube. John is still posting to YouTube, but on the site we are able to take advantage of Wistia and take back some of the “Google juice” that would otherwise be delivered solely to YouTube.

I’ll admit to being overly excited when I saw the first “rich snippet” attached to an egghead.io domain. Maybe I’m a nerd?

The Solution Part 4: General SEO

Wistia has the video SEO covered, but an interesting aspect of video is that while you can tell Google the video exists, their robots aren’t smart enough (yet) to analyze the video for keywords. To get over this hurdle, you can use transcripts of the video and provide the spiders the words they crave.

For this we used CastingWords. I was amazed, despite the videos technical nature, the transcripts we got back were very accurate.

Transcripts are a win/win/win. Lots of people would rather just read a block of text for speed. There are people that can’t hear at all, so audio content is useless to them. Search spiders love text.

Along with the transcripts we’ve been going through the backlog of videos adding summary descriptions to each. It is a chore, but it is worth the effort.

The Solution Part 5: Open Source isn’t Charity

The donation model in open-source software is flawed. It is not charity. It takes a lot of work to build high quality open source software. It takes a lot of work to write documentation and provide training.

We can do better than asking for hand-outs.

While lots of generous visitors (~500) have donated to egghead.io over the last year, we decided to take a different approach. Instead of simply asking for a donation, what if we bundled up the first 50 AngularJS videos and offered an “offline HD bundle” of all the videos instead? This way we aren’t accepting charity, and giving supporters something tangible for their hard-earned $$.

This approach has some limitations. All of the videos are streaming for free a back click away, but having the full resolution copies on your hard drive is something people want. It also gives a people a way to “donate” and get a receipt that can be expensed or deducted as a training cost.

It’s been a huge success. The lights will be on at egghead.io for quite some time. We will be able to expand the site, and explore a wider range of high quality content.

Conclusion and Next Steps

I love AngularJS. It is an amazing tool. It isn’t always the right tool for the job. If you are building web applications it might be a perfect fit. If you are building web pages the “single page application” approach has some severe flaws. Ruby on Rails solves these nicely.

Video provides many distinct challenges from a delivery and SEO perspective. YouTube makes it incredibly easy to share your content online, but the “cost of free” is that Google will leverage your content to take your audience away from your site and into their ecosystem. They give YouTube search priority, and squeeze your content for every drop of juice they can. By moving to a commercial hosting service like Wistia, you can regain a lot of control over the hard work you’ve put into your content.

We aren’t running a charity, and asking for donations is a habit I’d like to see broken in OSS. There are clearly better ways to be compensated for our efforts, and I know that I love supporting OSS. It makes my day to send a content producer $$ for a high-quality book, workshop, or other training. I get smarter, they get paid. Everybody wins.

Working on the relaunch of egghead.io has been a lot of fun. It has allowed me to put to use a lot of the things I’ve been learning over the last year in regards to SEO, conversion rate optimization, and generally building a modern web page. It is extremely awesome to have a project that I care deeply about, and can work on lovingly in my free time. I love my enterprise clients, but having a little techno proto-baby to feed and care for fills my heart with joy. Again, I might be a nerd ;)

Now I’m looking forward to publishing my first screencast to the site! It should be soon, so be on the lookout :>

P.S. If you’re looking to build an AngularJS site on Rails, I highly recommend this book.

]]>2013-08-18T12:45:00-07:00http://joelhooks.com/blog/2013/08/18/configuring-dependency-injection-in-angularjsDependency injection is the act of supplying values or object instances (dependencies) to target objects
from outside of the target object. In many (most?) cases this is automated
by a framework, such as AngularJS.

This means that a given target object does not create its own dependencies,
through the use of the new keyword or other creation methods.

By creating and managing dependencies
outside of an object, it makes it much easier to switch out that dependency as
needed. This is very useful when you are writing your unit tests, and can have
many advantages in larger systems.

There are only three ways for an object to resolve its dependencies:

internally, via the new operator

lookup via a global variable (requirejs is an example)

the dependency is passed to the object

The third option is dependency injection, and it is the preferred approach in
AngularJS apps.

Defining your dependencies

Dependency injection is a core feature of AngularJS. There are 3 approaches to
defining your dependencies, ordered by complexity from least to most:

module.service

module.factory

module.provider

note:AngularJS also provides value and constant dependencies. We aren’t
going to get into those two today.

Both service and factory are abstractions that sit on top of provider.
Using factory and provider will give you more flexibility, but are more
verbose.

Before we look at how to use these tools, let’s take a look at the AngularJS
source code and understand how they work.

Interlude into the AngularJS internals

If you’re a geek like me, you might be curious as to what is going on under the hood
when you declare dependencies.

I mentioned earlier that service and factory were abstractions on top of provider.
To show you exactly how that works, we need to open up injector.js in src/auto/
folder in the AngularJS source code:

As you can see at a glance, service calls factory which calls provider. So, when it
gets right down to it, these three methods are the exact same thing. Convenient!

Almost the exact same thing.

There is a subtle difference. The AngularJS service uses
$injector.instantiate on the constructor function that you pass in. This means
that internally the service creates an instance of your function with the
new operator. This will provide the resulting object a valid ‘this’ scope.

Using factory doesn’t call new on the function that is passed in.
When using factory, the function that is passed in is called directly, and an
object is expteded to be returned.

Hat tip to
@ThomasBurleson for pointing this
out. This can be confusing if encountered in the wild. Now you know.
Half the battle.

In this example we are creating a module that will store a model that grabs data from some asynchronous
service. The myModel service will return an instance of MyModel when it is requested
for injection by other objects. The instance of MyModel is a singleton, and only one instance will
ever be created and used by the application.

This example could actually be even simpler if the injectable doesn’t require any additional
dependencies.

1

module.service('myModel',MyModel);//most simple option

module.service requires only two arguments. A string for its unique name, and a constructor
to create an instance of. This approach is useful, and most of the time is probably all you need
for your application.

When you need more flexibility than the service provides, it is time to look at factory.

Defining a factory in AngularJS

A factory returns an object.

123456789101112131415161718

(function(angular){varmodule=angular.module("myApp.myModel",[]);varMyModel=functionMyModel(asyncService){return{someApi:function(){returnasyncService.getStuff();//promise?}}}module.factory('myModel',['asyncService',function(asyncService){//could do some stuff herereturnnewMyModel(asyncService);}]);}(angular))

Using a factory provides additional flexibility. By providing a factory function
over a straight constructor, you are provided with the opportunity to do some work
prior to returning the object. You are also in charge of creating the
instance that you want returned, unlike service, which creates the
instance for the constructor function you provide.

The above example is obviously not
doing anything interesting, but when you need to do some work prior to resolving a
dependency, a factory can be a good choice.

In the real world, I’ve used factory to provide a configurable mock data “mode”. The
factory function would check to see which mode the app was in, and dynamically switch
between mock and real data. This can be incredibly handy when you want to work with out
depending on external services.

Note that the factory function will be called exactly one time. Any work you do
will only be done once, and myModel will be whatever your factory function returns.
In this case, we are simply returning an instance of MyModel, but a factory can return
objects and functions. Use that to your advantage.

The last way to define dependencies is with provider. Let’s look at that next.

Defining a provider with AngularJS

123456789101112131415161718192021222324

(function(angular){varmodule=angular.module("myApp.myModel",[]);varMyModel=functionMyModel(){return{asyncService:null,someApi:function{returnthis.asyncService.getStuff();//promise?}}}varmyModelProvider={model:newMyModel();$get:['asyncService',function(asyncService){this.model.asyncService=asyncService;//"manual" dependency injectionreturnthis.model;//resolved for the lifetime of app}]};modules.provider('myModel',myModelProvider);}(angular))

As you can see, provider is lower level. Explicit and verbose. The $get function
is used by AngularJS internally for the injector. A provider is required to have a $get
function. When using factory (and service as well) the $get function is defined for
you.

For all practical purposes you will likely never need to use provider unless you are a
control freak. In most circumstances a factory or service will suffice, but it is nice
to know that provider is there, if you needed that level of explicit control for some reason.

One thing to note about providers is that the provider is available during configuration phase of a module. While I haven’t found a specific use case for this, it is something to have in your toolbox.

A little trick for dynamic dependencies

I mentioned before that with a factory (or provider) you can return an object or a function.
As it turns out, this can be very useful if you need to dynamically update a resolved dependency.
Here’s a simple example using a factory.

This is an extremely trivial example, but now when you inject myDynamicInjectable and call it,
it will return the freshly incremented count.

warning:don’t do this. There are two things wrong with this example. It is storing state
with the count variable, and then it is manipulating state. This isn’t the appropriate location
for either of those activities! A better solution would be to create an object that stored
that state and provided a nice API for manipulating it.

A more realistic (useful) use of this might look like this:

1234567

module.factory('getCurrentShoppingCart',['getCurrentAccount',function(getCurrentAccount){returnfunction(){//getCurrentAccount is also a factory that returns a function//perhaps a user can have multiple accounts?returngetCurrentAccount().shoppingCart;}}]);

There is a ton of dynamic flexibility you can take advantage of when returning functions from
both the provider and factory approaches. I’d proceed with caution. You could easily abuse this
flexibility. Don’t use this approach to manipulate and/or store state in the providers! The
job of these tools is to resolve dependencies, and should be used only to resolve dependencies.

Conclusion

AngularJS provides several ways to configure dependency injection. From the simple service to the more flexible
factory and provider approaches. You should have a solid understanding of how these work
under the hood, and what situations are appropriate for each method.

P.S. This article is the expansion of the answer to a question that was emailed to me.
If you have any questions, I’d love to help you out. My email and twitter can be found below, and I
answer them all.

]]>2013-08-03T23:36:00-07:00http://joelhooks.com/blog/2013/08/03/learn-angularjs-in-a-weekendAngularJS has a reputation for a steep learning curve. It’s definitely complex, but follows the 80/20 rule. 20% of the features are what you will use 80% of the time. If you are new to AngularJS and have a weekend to study, there are some very high quality resources that will let you start Monday with a strong working knowledge of AngularJS.

The Official Tutorial ~4hrs

Start with the official tutorial. It provides a very straight forward overview of AngularJS and walks step by step through building a simple app. When you’re done, you should have a solid understanding of the basics and the vocabulary you will need to move on to the next step.

The official tutorial is very well put together. Unfortunately it utilizes the angular-seed project as a template. The angular-seed project is fine for quickly throwing together a working AngularJS app, but it doesn’t provide a structure that scales well in a production application.

Kickass (free!) Video Tutorials on Egghead.io ~3hrs

You’ve done the tutorial, and should have a good grasp of the basics. Your next stop is John Lindquist’s egghead.io for free AngularJS videos. This series of bite-sized videos will deliver a ton of great information. John has gone through almost all of the “hard” concepts, and presented them in an easy to understand way.

All of the videos are great, but the series on directives is extra fantastic. Transclusion? No problem. Even after using AngularJS for over a year, I was able to solidify some of the concepts that were confusing for me.

Watch them all and take some notes. When you come out of the other side your AngularJS-fu will be starting to take shape.

That will take you through Saturday evening, and you deserve a rest. Send John a donation for his hard work, pour a tall glass of lemonade, and relax as you reflect on the your new-found knowledge of an awesome framework. Nice work!

Things get real with angular-app ~4hrs

Sunday morning.

At this point you should understand the core AngularJS concepts and terminology. Time to brew a fresh pot and get to work.

If you are anything like me, by now you’re ready to dig into a proper example application. Luckily we have the excellent angular-app project to explore. This non-trivial example focuses on best practices for:

Folder structure (important!)

Modules (very important!)

Testing (super important!)

RESTful services

Navigation

Security

angular-app combines a solid AngularJS UI with a node.js backend. It is non-trivial, and studying this app will give you a realistic sense of a proper AngularJS app.

One killer aspect of this example is the build system. It demonstrates a fantastic Grunt.js build with an integrated Karma Test Runner.

Peter Bacon Darwin and Pewel Kozlowski have done a great job. Front to back, this project is worth your study time. You could spend several days investigating the nooks and crannies of this example, but we’ve only got the weekend. Set angular-app to the side for now, but keep it handy. It will serve as valuable reference in the future.

Start building your own app with ng-boilerplate ~4hrs

At this point you should have a solid knowledge to start building something. The ng-boilerplate project will get you started. This is the seed you should use. It takes the lessons you learned studying angular-app, and provides the foundation upon which to build something substantial.

ng-boilerplate, unlike the angular-seed project, is suitable as a starting point for building a production app. It’s a solid shortcut, and worth study.

Spend some time getting to know ng-boilerplate through its excellent README. The READMEs don’t stop at the root of the project. Josh has sprinkled them throughout the project to help you understand what is going on.

Once you have your head around ng-boilerplate, you can delete all of the placeholder views, and start trying things out for yourself.

Need some ideas?

PRISM:Refract – NSA Dashboard

FlySwatter – A simple bug tracker

GeekTalk – Multiroom Chat

OctoStats – Ranked Github Users with Clever D3 Visuals

1000words – Writing app that sets target of 1000 words per day and graphs your success

GetItDone – Yup, a todo list.

…?

The sky is the limit. Using the reference material you’ve studied all weekend, combined with the official AngularJS docs, you should be well on the way to AngularJS mastery. You’ll be over the hump, having defeated that infamous curve. Build something cool and tell me about it.

Congrats! You’ve leveled up.

The fun isn’t over. There is plenty left to learn. The weekend is over though, and you’ve worked hard. Take a break and relax.

You’ve earned it.

P.S. Initial project setup is one of the most critical factors to a project’s success. Using templates is a great way to get a head start, but using a template without solid understanding of all the moving parts can be dangerous. I’m working on step-by-step guide to building your own best practices AngularJS project template. If you want to get notified of its progress, as well as launch-day discounts, sign up to my newsletter below.

In an effort to better understand
@joshdmiller’s excellent
ng-boilerplate, I
wanted to understand its dependency on Boostrap. More specifically, I wanted to
see if I could swap out Twitter Bootstrap for Zurb Foundation. Bootstrap is
great, but I really love using SCSS.

Down the rabit hole…

Up until this point, I’ve completely ignored
angular-ui-bootstrap, which is a wrapper for
Twitter Bootstrap that you can use with AngularJS. It hasn’t been on my radar,
simply because I prefer Foundation. That was a mistake. This wonderful little
library is amazing. On many levels, it expresses the power and flexibility of
AngularJS like nothing else I have seen.

We are aiming at providing a set of AngularJS directives based on Twitter
Bootstrap’s markup and CSS. The goal is to provide native AngularJS directives
without any dependency on jQuery or Bootstrap’s JavaScript. It is often better
to rewrite an existing JavaScript code and create a new, pure AngularJS
directive. Most of the time the resulting directive is smaller as compared to
the orginal JavaScript code size and better integrated into the AngularJS
ecosystem.

This struck me.

The only dependency that ui-bootstrap has on Twitter Bootstrap is the CSS style
sheets. All of the widgets work because they have been implemented with
AngularJS directives.

I was reading through some posts on the Google groups in my earlier quest to
find out how to integrate Foundation into ng-boilerplate (I’m stubborn!) and was
very interested in what Josh had to say:

You can wire up some callbacks and $apply calls to make a jQuery
plugin work but as Pawel said, rewriting something in AngularJS often takes less
work. jQuery doesn’t have any of the binding or scope magic. When we cut out all
of the jQuery code that makes up for that, we’re often left with very little
code. And when we put those few lines of code in an AngularJS directive,
everything will work out of the box. So in balancing levels of effort, rewriting
makes sense more often than it doesn’t.- Josh
David Miller

Mind blown.

In the post quoted above Josh also links to his excellent Stack
Overflow
post that expands on this viewpoint. You may have seen this already, but if you
haven’t, go take 10 minutes to read through it.

Don’t even use jQuery. Don’t even include it. It will hold you back. And when
you come to a problem that you think you know how to solve in jQuery already,
before you reach for the $, try to think about how to do it within the confines
the AngularJS. If you don’t know, ask! 19 times out of 20, the best way to do
it doesn’t need jQuery and to try to solve it with jQuery results in more work
for you.

Bold words.

I’m convinced.

jQuery is a crutch if you are writing AngularJS applications.

If you’re starting an AngularJS app, take a good look at
ng-boilerplate. Then take a look at ui-bootstrap’s directives. They are a living
example of how you can do “jQuery things” with a fraction of the code, and build
an app that is easier to maintain, way more testable, and generally nicer to
work with.

P.S. If you were wondering, it is theoretically possible to simply use the
Foundation CSS with ui-bootstrap. There is some work being done in that regard,
and I’m looking forward to pitching in on it. I don’t know that it will ever get
to “drop in replacement” status, but from the discussions I’ve read the future
looks promising on this front.

P.P.S This isn’t a critique of jQuery. I think jQuery is awesome and has moved
the web forward considerably. Even within Angular, they use what is called
“jqLite” to give the core essentials of jQuery’s functionality. In that sense,
just using Angular in the Angular way uses jQuery, but a minimal subset of it.

]]>2013-07-22T10:55:00-07:00http://joelhooks.com/blog/2013/07/22/the-basics-of-using-ui-router-with-angularjsURL routing is a popular approach to matching the contents of a URL to specific
functionality within a web application. URL routes programatically present
specific content to users based on the URL that they are visiting. It is a
popular approach that has proven to be very effective.

Something that might not be obvious is that URL routing is also a finite state
machine. When you configure
the routing for an app, you are laying out the various states the application
can be in, and informing the application what to display and do when a specific
route is encountered.

AngularJS supplies URL routing by default.
It is adequate, but also has some limitations.

So what’s the problem?

Looking at the structure above, imagine an application where interacting with
items on the header or the sidebar causes the main content to load completely
different HTML. AngularJS provides a mechanism for this with ng-switch. You
can see a (very simple) example of this below.

In this case, ng-switch is swapping out div elements, but you can also use
this approach to swap out templates using ng-include.

I’m not going to explore this option. I’m not particularly fond of it.

Why not?

The ng-switch adds markup that could be confusing

The state of the main content area is captured and stored on a model

It feels like “logic in markup”, which I try to avoid

If you go down the ng-include road, you need to remember to always put
single quotes around your template names. I always forget. ;<

ui-router

ui-router fully embraces the
state-machine nature of a routing system. It allows you to define states, and
transition your application to those states. The real win is that it allows you
to decouple nested states, and do some very complicated layouts in an elegant
way.

You need to think about your routing a bit differently, but once you get your
head around the state-based approach, I think you will like it.

This example is functionally very similar to the ng-switch approach. The main
difference is that the main content area is populated with templated HTML for
each of the states, without using switching or ng-include.

The above code is the configuration for the router. We are defining the module, but instead
of injecting $routeProvider as we would with stock AngularJS, we are injecting
a $stateProvider that is used to define the states. We are defining 4 states.

home is the parent state of the next 3. It defines the header, siderbar, and
the ui-view element that will be populated with the child states.

red is the first child state. It references the home as its parent, as
well as targeting its own template.

blue and green are identical to red, but use different templates.

After the states are defined, they are added to the $stateProvider. They are
now ready to be navigated to.

123

.run(['$state',function($state){$state.transitionTo('home');}])

the run method is a great place to navigate to your intial state. You are able to
inject the now-resolved $state object and use its transitionTo method to set home
as the current state.

The SidebarCtrl lists contains a simple array called content on its $scope which
is used in a ng-repeat to give us a simple menu. When one of those items is clicked
the setPage function is called with the item that was clicked. In this case, the
content items correspond to the names of the states that we’ve defined, so we simply
transitionTo the selected state to display the content of that state.

Next steps with ui-router

This only really scratches the very surface of what you can pull off with
ui-router. There are a ton of options and the
wiki is well put together. Some
things I didn’t explore, but that are available:

Passing data between states

Listening for state events

Named views (love this, it deserves its own post)

URL Routing

The ui-router README warns that the utility is still in active early
development. The API is still subject to change, so if you choose ui-router for
your next project, that is something to be aware of.

Let me know if you’d like me to explore ui-router a bit deeper in future posts.
I think it is an excellent approach to routing, and look forward to using it
more.

]]>2013-07-15T13:22:00-07:00http://joelhooks.com/blog/2013/07/15/a-look-at-angularjs-internal-directives-that-override-standard-html-tagsDirectives are the heart and soul of AngularJS. They are incredibly powerful.
AngularJS sets out to extend the grammar of the browser to supply semantics that
facilitate the creation of web applications, going beyond the standard
hyper-linked web page. The primary weapon to accomplish this is the directive.

Creating your own directives is an awesome way to create composed, reusable
behaviors in your applications. This article isn’t about making your own
directives, instead I wanted to take a closer look at the internal directives of
AngularJS. Specifically, we will be looking at how and why AngularJS extends the
functionality of native HTML tags like <input> and <a> to make the magic
happen.

A is for Anchor

1234567891011121314151617181920212223242526272829

varhtmlAnchorDirective=valueFn({restrict:'E',compile:function(element,attr){if(msie<=8){// turn <a href ng-click="..">link</a> into a stylable link in IE// but only if it doesn't have name attribute, in which case it's an anchorif(!attr.href&&!attr.name){attr.$set('href','');}// add a comment node to anchors to workaround IE bug that causes element content to be reset// to new attribute content if attribute is updated with value containing @ and element also// contains value with @// see issue #1949element.append(document.createComment('IE fix'));}returnfunction(scope,element){element.on('click',function(event){// if we have no href url, then don't navigate anywhere.if(!element.attr('href')){event.preventDefault();}});}}});

The htmlAnchorDirective has a simple job. It is there to prevent navigation and page reloading. Typically this is in conjunction with ng-click, which is used to actually capture the click and navigate the user within the application. Every <a> in your application is effectively extended by AngularJS. The functionality is primarily the event.preventDefault() that is applied if the anchor tag doesn’t have an href attribute.

One thing to note, and this is typical throughout the AngularJS internals, is that this directive requires special attention for IE 7. When I’m digging around in the internals, I’m always appreciative of this effort made by the AngularJS contributors. These aren’t fun problems to solve, and it is nice that somebody has made the effort to solve them for us :>

Digging into <form>

AngularJS overrides <form> to provide some important functionality. The core of this extension of <form> is to prevent any page refresh that would occur with an unmodified <form> tag. Lets have a look:

varformDirectiveFactory=function(isNgForm){return['$timeout',function($timeout){varformDirective={name:'form',restrict:'E',controller:FormController,compile:function(){return{pre:function(scope,formElement,attr,controller){if(!attr.action){// we can't use jq events because if a form is destroyed during submission the default// action is not prevented. see #1238//// IE 9 is not affected because it doesn't fire a submit event and try to do a full// page reload if the form was destroyed by submission of the form via a click handler// on a button in the form. Looks like an IE9 specific bug.varpreventDefaultListener=function(event){event.preventDefault?event.preventDefault():event.returnValue=false;// IE};addEventListenerFn(formElement[0],'submit',preventDefaultListener);// unregister the preventDefault listener so that we don't not leak memory but in a// way that will achieve the prevention of the default action.formElement.on('$destroy',function(){$timeout(function(){removeEventListenerFn(formElement[0],'submit',preventDefaultListener);},0,false);});}varparentFormCtrl=formElement.parent().controller('form'),alias=attr.name||attr.ngForm;if(alias){scope[alias]=controller;}if(parentFormCtrl){formElement.on('$destroy',function(){parentFormCtrl.$removeControl(controller);if(alias){scope[alias]=undefined;}extend(controller,nullFormCtrl);//stop propagating child destruction handlers upwards});}}};}};returnisNgForm?extend(copy(formDirective),{restrict:'EAC'}):formDirective;}];};

The above function is a factory that creates a form directive. The directive itself does several things. Aside from some memory management it also serves to prevent the default behavior of the form action. Typically with an AngularJS application, you will want to capture the user’s input it a form, and feed that data into a controller to send it to the server. This is different from the standard action attribute of a form that will perform a POST operation and typically redirect the user to a new page. This behavior is probably not what you want in your single-page JavaScript application, so AngularJS is working to help prevent that. You probably still want to be able to submit your form, and the ngSubmit directive placed as an attribute on the <form> tag will execute an expression when your designated submit input is clicked.

If you’re paying close attention, you’ll notice that the form directive above has a FormController assigned to it. The FormController is the brains of all the forms within an AngularJS application, and every <form> gets one. The FormController tracks all of the controls within a form and manages the validity of the form.

Simple stuff. It is using the REGEX above and setting the validity on the FormController, which you can then use to display feedback to the user. Email and number validation works in a similar fashion.

With text-type inputs AnglularJS also provides data-binding via ngModel, which is an extremely convenient solution to capturing user input and displaying it in the form. We should look at ngModel a bit closer in a future post.

Just the beginning.

It wasn’t immediately obvious to me when I started using AngularJS that the framework was overriding these default HTML tags to add the secret sauce on top. Once the realization dawned on me, it opened my eyes to the power and potential that directives hold. You aren’t restricted to the extensions that AngularJS provides with these built-in directives. You can further extend the capabilities of HTML by creating your own directives that override and extend the native HTML elements.

Digging into the guts of the AngularJS source code is a great way to learn the hows and whys of the framework, and can reveal techniques that can be applied to your own applications. The AngularJS source is well documented, cleanly written, and well tested. If you’re working with AngularJS, I highly recommend diving into these internals and discovering this for yourself. It won’t be time wasted.

]]>2013-06-25T20:04:00-07:00http://joelhooks.com/blog/2013/06/25/why-doesnt-my-photography-studio-website-appear-in-search-resultsThis is a series of post examining why our Fort Worth photography
studio website performs poorly in Google search
results, and why it fails to convert the visitors it does get into new clients.
The first post sought to analyze Google Analytics
data
and get a clue as to how users arrive to the site via search. The short story is
that they typically arrive via image searches for very generic terms such as “4
month old baby” and other similar searches. Even amongst these searches, actual
clicks through to the site are abysmally low. I’m working on a book about
conversion optimization for photographers, and our
studio is the first case study in the process.

These are some interesting phrases. I can almost remember writing them.
First, the business name. The site does really
well when searching for “Visual Empathy”, which of course nobody does. It has
“Dallas Fort Worth” in the title, which is definitely where we are at, but they
are in isolation. I thought these two were interesting:

Fine portrait photography

Modern Lifestyle Portraiture

Why are they interesting? Because they’ve both been searched for by nobody
ever. “Children’s Portraits” and “Portrait Photographer” are ok, but it is a
bunch of disjointed keywords that gives Google’s robots no specifics to hone in
on. Robots really like specifics.

Walking down the page, we get to the header. The logo is actually a background
image, which misses an opportunity to use the alt text to let the robots know
what the page is about. Image alt text is actually important, and was something
that I never even considered when I put this page together in 2007. Sliding
across the header is the navigation, which isn’t egregious, but is also
extremely generic and vague, targeting pages that have extremely generic and
vague titles. Again, missed opportunity to turn on the bright blinking lights
about what this page is for Google.

Next down the page is the gallery. My wife takes truly wonderful pictures of
people. The photography is beautiful, but it is
also hidden behind now dated Flash image gallery. While this isn’t really
hurting us in terms of SEO, it probably isn’t helping either. We are
definitely missing another opportunity to use the alt tags on the images. The
worst part about this presentation is that it totally excludes mobile phones.
Visit the page on an iPhone and you get a big white field of nothing. Not
cool.

Down below the fold is a mishmash. To the left is an area that uses a special
catagory of blog post to show a special. It isn’t helping with SEO at all. No
keyword phrases, no image alt text. The picture is nice, but otherwise…

Now to the right. 6 paragraphs of me being “clever”, using flowery phrases and a
smattering of keywords. The last paragraph has the potential to actually harm
our SEO. Google robots hate keyword packing. The same word, photographer in
this case, repeated over and over again.

Digging into the blog…

Blogs have a ton of potential for capturing the long tail of SEO. This is
where we can really produce some content, using keywords that highlight specific
terms and phrases that potential customers might search for. For a photographer
the blog can also serve as a gallery that uses posts as a way for clients to
pin, like, and share their pictures with family and friends.

I’ll use the latest post at the time of this writing that highlights some
beautiful wedding photos. The title is J&J Wedding- Dallas/ Fort Worth Wedding
Photographer. Looking down the page, they all follow this pattern. Client
Name- Dallas/ Fort Worth _________ Photographer. This is where we start to see
the origins of the image search results we talked about above. The post titles
are definitely affecting search results, but not a way that is useful for the
site’s SEO. I suspect the repeated use of the exact same Dallas/ Fort Worth
Photographer in the titles is adversely affecting search results too. That is
just a suspicion, but given the objective results (or lack of results), the post
titles aren’t helping. The image alt tags are also not contributing here. They
are the filenames. Nobody is searching for specific filenames when they search
for a local portrait photographer.

Kristina also got into a “blog circle” this year. This is a good idea. Their
implementation of the concept could use some work, but it could be a good thing.
The drawback here is that it effectively turned the blog into a personal blog,
akin to a Tumblr. I love my children, but rule number one of your photography
website has to be only post your best, and these once a week posts border
on snapshots at times.

And in conclusion.

The only redeeming aspect of the website is the photography. Period. Which is
great news. Given awesome photography, we can build something on top of that.
These first two posts are a little boring, but I wanted to deconstruct the site
to understand exactly what was wrong.

The really interesting bits are in the fixing, and that is what is coming next.
I’m terribly excited to start the process. Stay tuned!

]]>2013-06-22T08:32:00-07:00http://joelhooks.com/blog/2013/06/22/optimizing-a-photography-studio-website-for-converstion-and-seo-the-analysisThe internet is a competitive place for small businesses, and this is especially
true when we are talking about portrait photographers. My lovely wife is a
family photographer in Fort Worth, Texas. She’s been
doing portrait photography professionally since 2007, and being the good nerd
husband I am, she’s had a web presence the entire time. By this I mean she’s had
a website that I built in 2007 (prior to my own shift in careers from 3d
animation to web development), and haven’t really touched since. She’s been
left to her own devices, outside of my intervention when her Wordpress
installation was “hacked” to be used for nefarious purposes (this is an entirely
different post, ugh).

Fast forward to 2013. This year has been interesting for me professionally. I’ve
become extremely passionate about conversion optimization and search engine
optimization. Conversion optimization simply means converting website visitors
into paying clients, and search engine optimization (SEO) is the art of climbing
the Google ranks for organic search results. Our photography site is the polar
opposite of being optimized for either of those things.
My research on conversion optimization and SEO wasn’t focused on our photography
business, at all. It is a weird instance of the “cobblers son has no shoes”
where I wasn’t even considering our own business and how it might benefit from
the techniques that I’ve been learning. In fact, in my moment of epiphany it
occured to me that other photographer’s websites suffer in a similar fashion, so
I decided to write a guidebook on conversion optimization and SEO for
photographers.

I’m going to use my wife’s site as a case study, breaking it down and rebuilding
it into a useful asset that not only attracts potential clients, but converts
those visitors into high quality clients. Of the two, SEO and converstion
optimization, the latter is infinitely more important. Even if you have killer
placement in search results, or spend piles of cash to buy top placement with
AdWords, if those visitors aren’t contacting you in some way, then your website
is effectively useless. It is also a possibility that your website is
attracting the wrong clients, like bargain hunters looking for discount
photographs on the cheap. Visual Empathy’s website doesn’t do either, and the
first step is to take a look at why this might be the case.

The above is a search for “Fort Worth photographer”, which is fairly generic.
More specific searches don’t yield better results, unless you search for “Visual
Empathy”. Thankfully we land at number 1 there, but anybody searching for your
businesses name directly already knows about you.

We do have Google Analytics installed, so I can get some insight into what
searches are actually displaying the website. If you are curious, I’ve put a
full month of analytics data in this
spreadsheet,
but the top searches are:

4 month old baby

6 week old baby

rubys

12 month old baby

summer time photography

And the list goes on. Ugh. And to rub salt in the wound:

Normal web searches account for a tiny sliver of the pie. Images account for
the overwhelming amount of search results where VE actually shows up.
Searchers are looking for pictures of babies at certain ages. Only 0.75% of the
total search results actually end up getting clicked through. I actually think
this might be on the high side because there were some weird things in the
data. The items that were clicked all showed 5 clicks. That seems strange, but
the important thing is that nobody is coming to Visual Empathy’s website
because they are looking for a local photographer. Of those that
click through, exactly 0% actually convert to paying customers.

The silver lining? There is a lot of room for improvement.

In the next post in this series we will tear down Visual Empathy’s website and
try to understand why we are getting these results on Google, and attempt to
identify ways of improving them. As part of the series, Visual Empathy is going
to get a complete web makeover. Top to bottom, back to front.

Good question. One that might even make you chuckle[1] a little bit on the inside.
The word “enterprise” is definitely a loaded term. It could be considered by
some to be a buzzword. Others might say “Why are we talking about starships?”

When I use the term “enterprise” to describe software, I typically mean
“software that helps people get work done more efficiently.” In this sense,
Angry Birds and your favorite Twitter client are not enterprise applications. A
Twitter client that has additional features to facilitate and track a company’s
social engagement, on the other hand, might fall into the enterprise category.

An application that is used in a call center to track incoming support requests
is definitely “enterprise”. A system that connects several “legacy” systems and
presents employees with a unified interface and saves 1000s of man-years every
month of application context switching? Enterprise.

Enterprise software is software that is sold to a business or government agency,
and not to individuals. Content management, billing, point of sale, payment
processing, customer relations, help desk, project management, enterprise
application integrations, time tracking, business intelligence… you get the
idea.

This is software for getting things done. This is software that makes a business
money. Because of this, it is software that businesses will invest heavily in.

Enterprise isn’t cool.

Maybe. It certainly evokes thoughts of ugly user interfaces with huge J2EE
backends. Developers working on Saturdays to get their TPS reports filed.
Enterprise software certainly can be that. It doesn’t have to be.

Today’s software users, even enterprise software users, are more sophisticated.
They are used to using applications like Facebook and Gmail on a daily basis.
They have iPhones that present beautiful easy to use interfaces and excellent
user experience (UX). They want more.

More importantly, perhaps, is that software that provides excellent UX makes it
easier to get work done. Using software that sucks, is slow, is ugly, or
generally misbehaves is counter-productive. Bad software represents lost revenue
and increased overhead. Employees that are forced into bad software are forced
to focus their energy on wrestling the software. Not only does this make them
miserable, but it saps a business’s most valuable resource – the brain-power and
energy of their employees.

Enterprise can be cool.

It can! It largely depends on how you define “cool.” For me, my biggest thrill
in software development is eliminating cognitive overhead for people that are
trying to get work done. I want to make their working lives better. I want them
to be mentally free to concentrate on harder problems. I want their businesses
to make more money. I want them to see real value in their software, so that
they will keep hiring developers to build more software. Achieving these goals
is definitely cool.

Where does JavaScript fit in?

In today’s world, savvy enterprise customers demand rich experiences. They want
to be able to access their data anytime and anywhere. They carry smart phones
and tablets and expect their software to function on these devices as well as it
does on their desktop computers.
Arguably the quickest way to achieve this multi-screen approach is through web
applications. Applications that run, and run well, in any modern browser. You
don’t have negotiate walled-garden app stores to deploy a web app.

HTML pages originated as a format for linking scientific documents. This was a
great achievement, but soon users wanted more interactivity. JavaScript was
introduced, and now we could begin to see richer experiences, right in the
browser. This eventually brought us to DHTML, or dynamic HTML. The next
evolution in web pages was AJAX, or asynchronous JavaScript and XML. This is a
huge step, allowing us to hit the server for additional data without moving to
an entirely new page.

These days we’ve gone a step further with single-page web applications. A
single-page app isn’t a brochure-like web page for strictly delivering content.
It is a real application, built to do work. A single-page application is a
“thick” client that is more similar to a desktop application than a web page.

JavaScript has matured. HTML has matured. CSS has matured. All of these
technologies continue to see growth as their standards are updated to match
modern technology, and the demands of users.

Enterprises want to leverage these advances and build robust applications to
facilitate commerce. JavaScript is uniquely qualified as a programming language
to help with this. It allows us to harness the the computing power of modern
computers directly in the browser. In the past, we were forced to offload much
of the work for displaying a web page on to the server. A request was made and
the server generated a page that was displayed in the browser.

With modern JavaScript we are able to query the server for data via its
application programming interface (API) and use that data to dynamically update
content in the browser without actually changing URLs or reloading a page. The
client side application has its own templates and logic and can manage itself
tidily. The server is now a caretaker for data, while the application running in
the browser is entirely in charge of presentation and interaction with the data
that is provided.

JavaScript has been used in enterprise applications for many years. It is almost
as if we’ve come full circle. The major difference in modern applications is
that presentation concerns can now be completely offloaded to the client. We
aren’t beholden to a JSP or other server-side page template for rendering
content for our users. After the initial payload of the web application is
delivered, the server’s job is now reduced to handling requests for data. This
is still a big job, but the separation of concerns opens up a lot of
opportunity. Because the server’s boundaries are clearly separated from
presentation and focused on data, we are able to build APIs that can support
infinite possibilities in terms of how we present and interact with the data
that is flowing from the server.
This is awesome. This is opportunity. We are going to take advantage of this.

So what is enterprise JavaScript?

Enterprise JavaScript is used for building web applications that facilitate
commerce is some form or another. Enterprise JavaScript is likely going to be
larger and more complicated than a simple web page. Enterprise JavaScript is
probably going to be developed by a team, perhaps a large team or even several
teams, and will require stronger standards and practices than smaller
non-enterprise use cases.

Is it useful to tack on this “enterprise” label to JavaScript? Sometimes. When
you say that you are building an enterprise application, it definitely evokes a
mental image of what you are dealing with. Good or bad. You could say
“large-scale” JavaScript instead, but it wouldn’t be as specific. I’m an
enterprise developer, and I use JavaScript. Enterprise JavaScript.