Tuesday, September 1, 2015

Married to My Birthday

In honor of turning 0x1E today, I thought I would share with you my favorite web comic, Married to the Sea. In particular, I've curated the set of comics appearing on my birthday going back to the beginning of the comic in 2006. And at the bottom I included my favorite, even though it's not from my birthday, just for fun.

2006 - Beautiful Country

2007 - Cash Croppin in Kentucky

2008 - Fixer Upper

2009 - Gentrification Puppy

2010 - Books You Wanted

2011 - If You're Listening, God

2012 - Back on the Highway

2013 - What Would the Smurfs Do

2014 - Smoke Skulls

2015 - Boarded Up Window

My personal favorite:

What Are These Called

Tuesday, July 21, 2015

Interactive, Timed Programming Tests Make Little Sense

I've noticed a trend in my performance on programming and math interviews. Any time I have to do this sort of thing in front of someone else (in person, online, or over the phone), my solutions tend to be mediocre or worse, unless I am presenting a prepared solution. Yet any time I am given space and solitude to solve a long-form programming test I tend to do exceptionally well, even if it involves giving a presentation or other type of verbal explanation of the solution. And I almost always solve things both faster and more robustly when I am given solitude than when I must type out a solution in front of someone else.

I'm starting to wonder if the social aspect of coding or solving problems in front of someone is the main factor. In a job interview with someone you have never met before, when you're an introvert who is worried about how you come off socially, not saying the wrong thing, and so forth, it's like your brain RAM is taken up with all of these extra social processes, leaving less bandwidth for calling up your creative ability to solve some problems or even basic memory recall. I wonder if anyone has done experiments on whether interviewees exhibit better memory recall during time-constrained interviews or time-unconstrained ones, or with explicit vs. implicit indicators of time constraints, or when solving problems alone or in front of an evaluator.

No one likes to be seen making a mistake, so I have to believe that lots of little social biases creep in that throw off your whole way of working. When you're solving problems in the environment you find comfortable, you are at ease. You can make a mistake and it's ok. And that freedom lets your mind wander to the solution without worrying over artificial constraints, such as whether you're misspelling words in the documentation, or you type too slowly -- things that utterly don't matter from a technical point of view.

Obviously, an employee has to function in front of colleagues. But if you think about it, performing one's programming job never resembles the type of coding or programming that is tested during these interactive interviews. If you are given some kind of data structure trivia or a tricky probability riddle, then solving the problem really is all about silent focus, enumeration of the problem's details and constraints, and then diffing them against what you know about the problem already. In real work, this is when you close your office door, put on headphones, or take your laptop to the quiet room. You might have to do this recursively, you might try something, stumble and go back, and you might first provide rough sketches of solutions before going back to refine the bookkeeping details and corner cases. But the point is that you rarely, if ever, choose to do this with your friend or boss sitting at your desk with you. It's even less common for the nature of the work to require that the solution be dreamt up and executed in front of someone else. 

Even in a collaboration-intensive field like quant finance, which can often have tight intra-day deadlines, I never experienced any need to solve problems in front of other people. It was purely about receiving instructions from others, asking clarifying questions to nail down the needs and scope of the problem, executing the solution in solitude, and then coming back together with the project stakeholders and team mates to explain/share the solution and to consume explanations and solutions from those people for my own work too. In fact, I struggle to imagine how such busy intra-day deadlines could be dealt with otherwise. If you insisted on lots of pair programming and interactive solution-watching, your team's ability to solve multiple problems asynchronously would plummet.

It seems to me that all of the major traits of the main work for software problem solving are not social tasks. You don't do those things with audible narration on your first pass over the problem -- not even when you're doing something like pair programming. Which means that these sorts of problems are almost perfectly anti-correlated with someone's performance in a short, timed, socially awkward setting like an interactive coding session online or a phone interview. If my description is correct, we should expect to see a weak relationship between timed coding test performance and long-term job performance ... possibly even a negative relationship between the two if the skills that lend themselves to being capable of solving things auditorally anti-correlate with the skills needed for the more methodical, patient, creative process of long-term software problem solving.

If you contrast all of this with long-form code tests, the differences are stark. You'll still need to access knowledge about data structure trivia to approach the long-form solution, but you'll do it with whatever degree of solitude personally works for you. You won't do it interactively with a total stranger while being neurotically worried about how you sound (unless that's your thing). And, what's really nice, you'll still have to eventually talk to people and explain your solution. If you submit a bunch of mysterious code and you can't give a great account for how it works or why you made certain choices, the evaluators are going to be suspicious that you had outside help, or that your communication skills are not sufficient for the role, or that you don't truly understand how a certain approach works under the hood even if you recall some superficial details about it.

So with the long-form tests you get the best of both worlds. The programmer can write code in an environment suitable to his or her personality, without awkward social pressure brain processes being activated at the same time they are trying to solve a problem. And you can still probe them for any kind of explanatory communication you need to ensure that they really solved the problem and that they know how to relate the solution to others. You are measuring the kind of skills that actually matter on the job, while still having plenty of opportunity to assess communication skill.

One other benefit is that the solution process is not degraded by miscommunication. Often during timed, interactive programming tests, the assumptions or expectations are not clear to both parties. I once worked on an interactive interview that involved writing unit tests. However, the problem itself involved a hypothetical class that had not been written by anyone, and I was instructed to pretend that it had been implemented, with a particular set of attributes and methods, and to build my testing solution around that. None of it was documented in the problem statement beyond the name of the class to use.

But when it came to writing the unit tests, everything hinged on exactly how the class was implemented. I kept asking what kinds of things I could assume about the hypothetical class-whose-methods-mattered-for-testing, and the interviewer kept giving terse answers that did not address my questions. Likely we were talking past each other (I mean, we were complete strangers who had never worked together and only just been introduced 25 minutes earlier, trying to collaborate over a shared editor screen). But it easily caused 10 or 15 minutes of wasted time, dead air, re-asked questions, and so forth, all of which could have been avoided if the problem had been given long-form, perhaps even requiring me to do more work by implementing both the hypothetical class and the unit tests depending on it. Of course there might still be questions, but they can be rounded up after hours of careful thought, and then shared asynchronously by email so that no one is pressured in real-time to accept inadequate answers due to people talking past one another. 

As it turned out, however, my interviewer likely walked away from the assessment with a negative feeling about my ability to solve that sort of problem, and I walked away with a negative impression about the communication abilities of the other workers in that company. Both conclusions were probably wrong, but you can't unfeel that kind of awkward interview.

I'm sure that timed tests still have a place, I'm just not sure what that is in the world of programmer or quantitative researcher evaluations. Maybe they have to come out when the lower administration cost and shorter turn-around time for feedback are crucial, such as for extremely large firms that evaluate many candidates. Sadly, the test will be biased to favor extroverts whose natural thinking processes are not significantly disrupted by the social norms of a formal conversation with a stranger. Even in the best case they will be subject to degradation just due to common verbal misunderstandings, assumptions, clarifications, etc., in the presence of the time constraint. And overall this will have a population effect on the culture and type of programmer who will aggregate into companies that perform these sorts of evaluations.

Monday, July 13, 2015

The Unicorn Inequality

$$ Rhinoceros + Horse > Unicorn $$

A web developing rhinoceros working together with a machine learning horse is better than a full stack unicorn.

Let me share with you some of the ad copy for a job ad that I recently encountered:

As a Software Engineer (Python, Javascript) you will be expected to work on both the front-end, creating dynamic user interfaces, and the back-end by dealing with database access, distributed computing, and parallel computing. We are looking for an experienced software engineer with in-depth knowledge of the full software development life cycle who can contribute at multiple levels of the stack. 

This job is right for you if you are an analytical person and problem solver that can troubleshoot a complex application, come up with a good solution following best practices, and prove the accuracy of your code using automated testing. 

Requirements / Skills 
  • 5+ years experience designing and developing web applications from scratch featuring complex architectures
  • Experience with Javascript development using modern web tooling and workflows
  • Experience writing automated unit tests for front-end and back-end system components, functional tests, and integration tests.
  • Full understanding of the client-server architecture, from the browser all the way to the database
  • Strong understanding of software design principles
  • Experience developing large, scalable applications
  • Experience using Git for complex software systems with teams.
  • Experience using Linux or Mac for development
  • Passion for learning new tools, languages and frameworks.

  • Significant experience developing applications with Python
  • Several years experience with most of the technologies within our stack:
  • Flask
  • py.test
  • Gunicorn
  • AngularJS
  • Karma, Jasmine
  • SocketIO
  • NodeJS
  • Nginx
  • MongoDB
  • Redis
  • Ansible
  • Experience working in an agile environment with continuous integration using automated testing
  • Experience with NoSQL databases
  • Experience developing asynchronous distributed applications
  • Experience developing and deploying software for large enterprise customers
  • Experience or interest in data science and machine learning algorithms
  • Experience with Big Data technologies (Hadoop, Spark, etc.)
  • Experience with AWS
  • Experience using Vagrant and Virtual Machines

This here is what I like to call a Unicorn Job: it is a position that has some nominal defining qualities (in this case, scalable web development) but the true intentions underlying the position are clearly to try to find some sort of magician who can do everything. I mean just look at all those bonus items.

Let's think about it for a second. From the main skills for the position, it's clear that the ideal person needs years of experience with web development. This inevitably means they need to be an excellent, experienced Javascript and HTML programmer, as well as knowing some common web frameworks (such as, say, Django for Python) and various types of database backends for serving data. This alone is not an easy skill set to find: yes, web development is hot right now, but there's a big difference between someone who can sling a bit of Javascript and someone who has done it both at scale and for years.

Now let's take this already-above-average programmer and consider the bonuses. My, there are an awful lot of specialized technologies on that list. Some of them are probably easy to get: AngularJS and NodeJS are a popular Javascript framework and a popular Javascript runtime engine, respectively, that many experienced web engineers are likely to be familiar with. Nginx is also popular (it's an HTTP server with a lot of extras). So maybe these aren't too bad.

Well, now we also want someone to know Redis, which is a distributed key-value storage system. That one is a bit less common, but not unheard of. It's widely used, but not necessarily by web engineers. We also want experience with other NoSQL technologies? Probably the ad means MongoDB and Cassandra. Not everyone uses these, but still we might hope that someone has.

Oh but we also want someone who knows Ansible, Vagrant, virtual machines, and enterprise customer deployment strategies.

Ok. I see what's going on. This must be a job ad for a DevOps (developer operations) role, with some light web engineering sprinkled in. DevOps engineers will need to be familiar with the administrative and connective aspects of lots of the tools we've mentioned before, so that must be why there is so much on the list. The ad must be embellishing the desired skill with web engineering proper, but that's OK -- it's hard to write an ad so we can be forgiving.

But wait! There's more! This person also should have at least an interest in machine learning, if not outright experience in that field. And also experience with big data technologies such as Hadoop and Spark. And also asynchronous programming (all of asynchronous programming, I guess? Who knows?).

I hope you don't need me to point out how ridiculous this is.

We need to take someone who knows and/or is interested in machine learning, asynchronous programming, big data, Hadoop, Spark, half a dozen DevOps tools, half a dozen databases, and then slap 5+ years of web development experience on for them to be considered an ideal candidate for this job.

Should they also speak 5 languages, publish weekly op-eds on Slate, spend 8 months of the year volunteering to feed orphans, and hold 57 patents for the pharmaceutical cures of rare diseases?

One of the umbrella terms for this sort of job is the dreaded Full Stack Developer. This term is somewhat new and it derives from the different components that make up a given organization's technology stack: you might have a "bottom" component where the data and data-intensive algorithms reside (database layer) and you will have some form of a "middle" client-server layer (web layer) and on "top" you'll have some form of frontend user interface (frontend layer). This is a wildly simplified version from any real software production shop, but the basic idea is the same: for some reason companies are increasingly interested in hiring workers who have the capacity to work in any arena of the technology stack.

This might sound good at first: who wouldn't want to kill 3+ birds with one stone? If you can hire one person who can fix your database, create your server-side request processing algorithms and create your client-side logic and frontend, why wouldn't you?

Let me ask a different question: wouldn't you like to have a single doctor who specialized in kidneys, and OBGYN, and brain injuries, and lung cancer, and pediatric medicine, AND infectious diseases, AND BONE MARROW TRANSPLANTS, AND ...

No. Of course you wouldn't. No one can be a specialist at everything (or, so few people can be that it makes little sense to bet the farm on being able to find and hire them). There's this little thing called specialization of labor that helps us separate different siloed concerns away from the distractions of other topics, allowing professionals of all sorts to gain deep expertise and further human knowledge and performance. When you've got a kidney problem, you want a kidney specialist ... even if you also have a brain injury and you also need a brain specialist. Putting your fingers in your years and yelling "lalalala" while you demand for a single human to be your expert in both kidney treatment and brain treatment isn't going to magically make it happen.

We can think of it in soccer terms too. Box-to-box midfielders are impressive soccer athletes. But a team of 11 box-to-box midfielders is going to lose every time to a team that has proper strikers, proper wingers, proper defenders, and a proper keeper. A soccer manager who seeks 11 people who can purportedly each play every position is going to lose her job fast.

Let's leave the medical and soccer analogies and just think about this in the software world itself. We would never let code complexity managers (e.g. developers) create monolithic singleton classes that grow purely by the attrition of any and all possible responsibilities. Such mega classes are one of the surest signs of bad, dysfunctional code poised to fail and cost everyone much more money in the long run than what any short run savings could reasonably be worth. In software we call this specialization separation of concerns and we live by things like the single responsibility principle. We require heavy evidence about reasons to change. And we spend time meticulously refining interfaces (analogous to job descriptions) to ensure that different specialized system components smoothly work together and cover all of the needs.

But when we bump out of software complexity world and into human complexity world, this all goes to shit immediately. Managers seem to love creating monolithic singleton positions where a single unicorn hire will be expected to do it all. That person's responsibility set will grow primarily by attrition and is generally capped only by the literal limits of physical exhaustion, at which point the company whips together another pan-everything job ad to try to get a second copy of this unicorn and do it all over again.

In reality, if you did find [1] a developer with 5+ years of significant web engineering experience who also knew several databases, several DevOps tools, and machine learning and big data techniques, you'd have to be prepared to offer that magic unicorn developer a huge salary, and probably also let them come to work in cartoon pajamas. Since most companies looking for this sort of person are poorly managed start-ups hoping they can reduce headcount costs by cramming more and more bullet points into a single position description, you can bet they are not looking to pay the unicorn a high salary. So then how can they ever find one?

Well the catch is that you don't find a unicorn. You find someone who claims to be a unicorn and can do just enough to smooth talk around the interviews or assessments needed to get the job. Michael O. Church nails this one in his post on The Haskell Tax (emphasis mine):

... [companies] refuse to respect specialties once they’ve hired people, and people who insist on protecting their specialties (which they had to do to get where they are) are downgraded as “not a team player”. Ten years of machine learning experience? Doesn’t matter, we need you to fix this legacy Rails codebase. It’s ridiculous, but most companies demand an astronomically higher quality of work experience than they give out. The result of this is that the game is won by political favorites and self-selling douchebags, and most people in either of those categories can’t really code.

This is one of the reasons start-up culture is so deplorable right now. You can almost guarantee that any developer who cared so little about his or her own specialization so as to seek out and agree to work in a "full stack" role almost, by definition, must be blowing smoke about his or her actual coding ability. (I'm willing to cut some slack to those poor souls who join a firm and then get pressured into becoming more of a full stack developer due to shifting company policies -- but even so, it's a signal that the smart folks run away from).

I propose that we software developers unite and stop letting managers use this false language as they say "full stack development." Instead, let's use the proper name: Full (of) Crap Development.

But if companies end up only getting self-selling douchebags to fill their Full Crap roles, won't they eventually figure this out? Unfortunately, this assumes that the managers in charge of Full Crap Development actually care about the quality or encapsulation of the software systems they work on. In practice there is a much more insidious goal revealed by these types: outright refusal to engage in the management task.

One major hallmark of a good manager is that she protects her subordinate team members from interfacing with work requests that are not part of the job description. Good managers do not merely try to make this happen when it's convenient. Imagine if a subordinate programmer said she would try to write some code as long as it was convenient. It would be pure failure to perform a basic job duty and would be accordingly punished. Yet bad managers get away with this all the time. If they can dupe an overconfident and cheap youngster into agreeing to be repeatedly jerked around about what their actual job duties are (all under the guise of "be a team player" or "this is just what you have to do in start ups" or some other bullshit excuse), then they can save on headcount and they (believe they) can basically use manager auto-pilot. Throw a couple of Full Crap developers into a Scrum team and boom! touchdown!

Well-organized teams use job descriptions as planning items. They are not catch-all buckets of open-ended every-possible-thing sort of work. They meaningfully define what a given employee will do as much as they define what that employee will not do (crucially, what an employee can say no to).

The constraints from the will-not-do parts might even be more important for the success of the team, because it forces managers to actually contemplate the workload that Mother Nature is generating through the business process (customers, deadlines, shipping something, whatever). You then have to diff those against job descriptions and team descriptions and figure out how to chop the work up and distribute it to the right people. It's creative optimization in the face of constraints. That's a damn hard job and it's the primary part of the manager's job that entitles them to higher status and wealth within most organizations.

But surprise, Full Crap Development and unicorn chasing lead to managers who do pretty much none of this. You don't have to do the hard work of creative optimization if you define every team member to be a receptacle for every type of work. It leads to overworked employees who are vastly overqualified for some of their job yet vastly underqualified for other parts, leading to a worse on-average job performance than if human beings with specialized skills were actually organized accordingly. Not only is this bad for the employees, whose specializations are not respected and whose workloads are too burdensome, but also for the company which is not coming remotely close to extracting the full value it could from specialized employees who coordinate their efforts across different computing domains.

So how do we fix this? The overwhelmingly most important thing is for developers themselves to absolutely boycott and vociferously rail against this Full Crap trend. If a company places up a job ad for full stack development, don't just avoid that job,  but avoid that whole company. Even if the company itself ends up being financially successful, it's clear from their management commitment to Full Crap development that it's already beyond the point of failure from the point of view of quality of life, fairness, and efficiency. There are other good companies. Life's too short to be taken for a ride by managers looking for manager-level status and manager-level compensation but without actually doing the hard work of managing anything.

Hold managers accountable for your speciality. Even if you find yourself in a full-stack role today, don't allow lazy managers to get away with believing that this makes you an arbitrary work receptacle. Say no. Give accurate estimates when you are tasked with work that falls way outside of your specialization. Never done web service architecture but suddenly have a bunch of it to do? Instead of half-assing it by copy/pasting from some blogs and spamming your issues onto Stack Overflow, just be honest that it will take extra weeks, or even months, for you to patiently go back, learn some fundamentals about the topic, practice on some smaller scale example problems, and put together a minimally acceptable solution. And in the meantime, all of your regular work is going to be vastly slowed down too. 

Manager doesn't like to hear it? Tough. That's precisely why they should have hired a systems engineering rhinoceros who can work well with a web designing horse, instead of whitewashing over the issue by claiming to have hired a magical unicorn who will do both things equally as well while costing them less.

[1] In my experience, finding a true full-stack developer is extremely rare. I've only worked with one or two people who would truly qualify. I've known many veteran programmers who are exceptional, and who could easily pick up the necessary extras to claim that they are full stack -- but most often they don't because they've got their speciality (generally technology that they grew up with) and typically they have no need or patience for whatever modern web and big data crap someone is looking for. 

I think a safe rule of thumb is that no one with fewer than 10 years of code-every-day job experience should claim to be a full stack developer. Yes, maybe once in a while there's a person with prodigical talent who picks everything up at a deep level. But it's so rare that you might as well ignore it. After the ten year mark, some people just want to stick to their areas of specialization and they have no need or desire to grow their skill by breadth. But some others, by that point, have actually had enough variety of on-the-job experiences that they really have learned how to solve problems at every part of the technology stack. Those brave souls (most of whom start out as very low-level systems engineers or embedded programmers) are the only ones who have any real right to claim that they are full stack ... and they are worth their weight in gold.

Tuesday, June 23, 2015

And Now For Something Completely Different: Marzipaste! ... and Some Vegan Adventures

I rarely crave sweets and now that I've been experimenting with a vegan diet for a few months (eliminating dairy-based ice cream from contention) my drive for sweets has been even lower than normal. But one thing I do enjoy is marzipan (both the delicious candy that I will sort of explain how to make and also the lovable Homestar Runner character from my youth).

I've also been experimenting (partly out of necessity and partly out of nerdy love for optimization) with hyper optimizing my monthly grocery budget. So I noticed I had a bunch of left over sliced almonds and I wondered what exactly it would take to use them for marzipan.

I consulted a number of easily Googleable recipes, but they all required almond flour and rose water, which are expensive, obscure, and not used in many other recipes, or else honey which I didn't have at the time and didn't want to break my optimization streak to buy. So then, like any sane engineer, I looked around my kitchen and I said, "hmm... what is sort of like honey?"

The glorious answer: St. Germain and chocolate chips! I mixed 1/4 cup of dairy-free chocolate chips with a dash of vanilla extract and about 1/6 cup of St. Germain, and simmered the mixture in 1/3 cup of water until the chocolate chips were melted, then I stirred in 1 cup of sugar and continued stirring the mixture while it simmered until the sugar was dissolved.

Separately I put 2 and 1/2 cups of sliced almonds into a small food processor and pulsed them until they were coarsely chopped. Then I added the not-quite-boiling sugar/chocolate/vanilla/St. Germain/water/Thor's blood mixture to the food processor and I processed on high for about 1 minute continuously. I stopped to scrape down the sides of the processor bowl and repeated additional 1-minute increments of processing until the mixture acquired a gooey almond paste consistency.

Warning! As you can see, I totally put more stuff into this tiny food processor than I should have and as a result I am pretty sure that I nearly burnt out the motor. Since then, I've invested in a larger food processor, but you could do equally as well by just making this recipe in smaller batches and combining them into the same storage container at the end.

I placed the mixture into a plastic-wrap-lined container and left it in the fridge over night. It did not develop the same firmness that typical marzipan has, though it was slightly more firm than almond paste or almond butter. It was definitely spreadable and so I enjoyed it on some English muffins... voila:

Not too shabs for outright refusal to buy any ingredients I didn't happen to already have on hand. And I think the St. Germain/chocolate chip mixture was a delicious substitute for honey.

While I have been a strict vegetarian for almost 5 years now, this recent foray into vegan cooking has been most interesting. I was prompted to try it out from a simple conversation with my brother (he is also a vegetarian/trial-vegan). He pointed out that pretty much the only source of animal products in my life was cheese. Particularly cheese on pizza. I'm fairly health-conscious about my diet and I love cooking new things, but pizza is one of those guilty pleasures that just will be a part of my life forever and there's no sense in trying to force myself to be otherwise. Pizza is just going to happen.

So I said to myself that if I could find some kind of vegan substitute for cheese that worked well enough on a vegan pizza, then I would give a vegan diet a try. After all, I already prefer soy or almond milk to dairy milk, I drink coffee black, and I don't care much for traditional dessert foods (except ice cream, but I'll get to that in a minute).

I found such a head-slappingly simple vegan cheese alternative, vegan Parmesan cheese, that at first I thought it must be disgusting. That's never stopped me from eating things before, so I went ahead and made a pizza with some roasted veggies, baby spinach, and a tomato sauce that I tweaked with some paprika, curry and garlic powder, and some herbs.

I must say that the vegan Parmesan substitute was delicious. It offered me everything I was looking for on a pizza and I'm certainly content to trade-off whatever extra satisfaction the dairy fat from cheese might provide in order to get the calorie reduction and avoidance of creating further economic demand for animal food products. Here's a photo using prepared pizza dough... I prefer rolling my own dough, but it requires use of a lot of flour and cornmeal to make sure I don't screw up the transfer to the baking stone -- a source of wasted supplies that I don't want to incur during my budget optimization experiments.

I mentioned ice cream above as well. That is one dairy-based dessert that seduces me. So I wanted to find a way to make a dairy-free alternative. I found this interesting recipe for Moscato ice cream and flipped my bad ass welder's helmet visor down, blasted a flame out of my bad ass welding torch that I just carry around, and said "let's do it."

For this, I researched a bit on baking substitutes for vegan cooking and decided that either silken tofu or coconut cream were going to be my best bets. I opted for coconut cream, but it was somewhat of a challenge. First, I walked to not one but two eccentric health food stores, and a Whole Foods market, nearby and exactly none of them had coconut cream in stock. They had plenty of coconut oil, coconut "manna", and cream of coconut, but these are wrong, as I politely told the grocery store clerks who insisted these were what I needed.

Finally I had to order coconut cream from Amazon and wait two g.d. days before I could make the ice cream! I also substituted cherries for blackberries which created a lot of extra work (e.g. it's not a good idea to just throw un-pitted cherries into a food processor; and even when you get them pureed, it is more like a jam than a juice so you have to force it through a mesh strainer to harvest juice from it). But in the end, simply replacing dairy cream with coconut cream worked extremely well. I used an immersion blender instead of a hand mixer. Because of the slightly different fat composition of coconut cream, you won't get exactly the same fluffy, aerated texture you would from dairy cream, but the mixture still freezes well. When serving it, I recommend taking a little bit out of the freezer about 15 minutes in advance, letting it melt a little, and then spooning that on top of the fully frozen ice cream. It helps provide a slightly creamier texture.

The last vegan item that I want to highlight is tofu ricotta. Wait. Let me correct that. Tofu Badass Rambo Ricotta. This stuff is amazing and equally as head-slappingly easy to make as the vegan Parmesan cheese. I used this recipe (the tofu ricotta section) and since I already had a bunch of prepared vegan Parmesan cheese, it was super easy. I did opt to use a food processor rather than hand mixing and I used the optional vegan cream cheese. With the food processor this turns out much creamier and packs a lot of flavor. I can easily imagine that if you season it differently, this can serve as the base for custard or pudding, or even be used as part of a pie filling. I used it to make a baked rigatoni recipe, and saved some left overs to add as an extra ricotta "cheese" topping to the vegan pizza I mentioned before.

So far vegan cooking has been delightful, tasty, and at least as cost-effective as my previous vegetarian grocery shopping (probably more so). Yum!