Skip to main content

· 8 min read

Wasp fundraise chart

Wasp is a configuration language (DSL) for building full-stack web apps with less code and best practices that works alongside React and Node.js. We are on a mission to streamline web app development while empowering developers to continue using the power of code and their favourite tools. We are backed by Y Combinator and engineers from Airbnb, Facebook and Lyft.

We are in Alpha (try it out)!Join our communityWork with us

Wasp was part of Y Combinator’s W21 batch, which took place from January of 2021 until the end of March.

At Demo Day, our product had a solid traction (200+ projects created, 1k Github stars, good ProductHunt and HackerNews feedback) but no monetisation yet, which is typical for open-source projects at this stage. Being based in the EU, we also didn't have a huge network in the Bay Area prior to the fundraise.

caution

I will try to refrain from giving "general" advice (as our fundraise is a single data point), and focus on the stats and specific things that worked for us. Keep in mind the same might not work for you - I recommend always taking advice with a pinch of salt to see what makes the most sense in your case.

As we approached our fundraise, we didn't really know what to expect. We had friends from the previous batch that raised a big round very quickly (even before Demo Day) and heard a couple of stories from a few other YC founders who were also quite successful, so we imagined it might go quickly for us too.

As you can see from the title, we had quite a journey with plenty of meetings that provided us a lot of input on how to improve our pitch, and maybe even more importantly, how to reach the right investors.

Here are our stats:

  • we spoke to 212 investors → that led to 250+ meetings
  • 98 days passed between the first and the last signed SAFE
  • 171 investor passed, 24 never responded, 17 invested

And here is how it all looked when laid out on a timeline: Wasp fundraise chart

Here are some of the things that worked for us:

We treated fundraising as a sales process (and stuck to it)#

Wasp fundraise funnel

This means we had a typical sales funnel - lead generation, selling (pitching) and following up:

  • Lead generation: it started with Demo Day of course, from which we got 100+ leads but none of them ended up investing (more on that below). After that we mainly relied on our YC batchmates to identify relevant investors and get the intros.
  • Pitching: we did a conversational pitch without the deck, but we had a Notion one-pager from which I would drop links during the conversation (to e.g. our traction chart, user testimonials etc.). It also worked well as investors would typically find it interesting and keep scrolling through as we talked, asking follow-up questions.
  • Following-up: we followed up once per week. I would usually "batch process" it each Wednesday. We used Streak to identify all the leads that I haven't heard from in over 7 days (there is a filter for that) and then manually emailed them.

We started with tracking everything in Google Sheets, but with the volume of leads it soon became hard to navigate them through the funnel. Then we switched to Streak (used their fundraising template, and modified it a bit) and that worked great. The most helpful thing for me was having a CRM that is integrated with gmail, that made the process much more seamless and gave us better overview of the funnel. As soon as I would receive an email I could see in which stage the investor is, and it was also super easy to add new investors straight from gmail - it saved us from the dreaded context switching and kept us focused.

Our pitch became much better after ~50 meetings#

We kept being critical of our pitch and kept a list of questions that we felt needed more work. We called it "creating narratives", e.g. why the right time for our product is now, presenting the team, or how we plan to monetise. We talked to other companies in the same space (devtools, OSS), investigated comparatives (big companies we compared ourselves too), talked to our angels who were domain experts and used all that to build a more convincing story.

I never intended to learn our pitch by heart, but after delivering it for 100s of times just that happened - both me and Martin (my brother and cofounder, who wasn't pitching but was always sitting behind me and provided feedback, especially in the beginning) knew it word by word and I realised how much more polished it sounds and how much more confident I felt compared to when we just started.

Our goal was to get to 100 no's#

After about 50 meetings (and about 20 VCs having passed on us) we started feeling a bit disheartened, as things didn't seem to go so easy as we initially expected. Then I chatted to a friend who also recently finished their fundraise and he gave me a tour of Streak - I saw their numbers and that over 150 investors passed on them! With that I realised our 20 passes were just the beginning and that instead of chasing yeses we should actually chase no's :) - they are more predictable, you'll get plenty of them and they will clearly show your progress.

We had 100+ leads from Demo Day - none of them invested#

This is probably pretty specific for our case, but it's how it went. Connecting with a startup on Demo Day is a very low-cost action for investors. Also, as many investors as there are on Demo Day, there are even more of them who aren't.

When we sorted through the connections we got, about 20% were a really good fit for us, meaning they invest in deep tech / OSS companies, have invested recently, invest in our stage etc.

We still met with pretty much all the interested leads, but we quickly realised that due to our product being deeply technical and the company being pre-revenue, only investors with engineering backgrounds were really interested because they could understand and get excited about what we do. That informed us to generate our leads with much narrower focus.

We looked at other OSS & dev tools companies in our batch, looked at who invested in them and asked for intros. Our batchmates were also in the fundraising mode, they knew how hard it can be and they wanted to help, so everything moved very quickly.

We learned not to spend time on non-believers#

As we learned to focus on the highly qualified leads, we also learned that it is very hard (impossible) to change somebody's mind. Plenty of investors liked u and what we do, but they were skeptical about e.g. market size or monetisation potential and made that clear from the start. Many of them were keen to keep chatting, wanted to meet our angel investors etc., but none of that helped change their mind and it was very distracting for us. I believe it is very hard to change somebody's worldview, especially in the seed stage when there is often no strong factual evidence to do so.

Passing through the "valley of death"#

As you can see on the chart, about two months in we barely passed $300k, and we had a whole month with no progress. At the same time, we felt that our pitch got significantly better and we were reaching investors much better suited for us. It was one of the most difficult times, seeing others close their rounds, but we decided to trust in the process and keep going until we have used all the resources we had. It was also the time our lead investor took time to do their own pretty extensive due diligence on Wasp, so although it looks like no progress was made from the outside, a lot of stuff was actually happening behind the scenes.

Suddenly, a few things clicked together from multiple sides and our round was quickly closed, even oversubscribed! It was truly a magical feeling to start closing investors in a single day, even during the first call, when previously it took us weeks to close our first $50k check. The big factor was also that our round was getting filled up and that of course motivated investors to move faster.

We compared ourselves to big, successful companies#

This is one of the best pieces of advice we got from YC partners about fundraising. In the beginning we didn't understand how important this was, but once the meetings started we realised this was one of the best ways to explain the potential of our company to investors. With the innovation in technology that isn't easy to grasp, they needed something to hold on to understand how the business model and distribution could work, and it sounds much more doable if there is a playbook we can follow rather than us reinventing that as well. We kept working on finding a good comparable (we had a few) and explaining in which ways we are similar and why.

Good luck - you can do it!#

I hope you found this helpful and that our story will motivate you to keep going once things get hard! We wish you the best of luck and also feel free to reach out if you'll have any questions.

We are in Alpha (try it out)!Join our communityWork with us

· 5 min read

Wasp is a configuration language (DSL) for building full-stack web apps with less code and best practices that works alongside React and Node.js. We are on a mission to streamline web app development while empowering developers to continue using the power of code and their favourite tools. We are backed by Y Combinator and engineers from Airbnb, Facebook and Lyft.

We are in Alpha (try it out)!Join our communityWork with us

After graduating from Y Combinator's Winter 2021 Batch, we are super excited to announce that Wasp raised $1.5m in our first funding round! The round is led by Lunar Ventures and joined by HV Capital. Also see it in TechCrunch.

The best thing about it is that the majority of our investors are either experienced engineers themselves (e.g. ex-Facebook, Twitter and Airbnb) or have a strong focus on investing in deep technology and developer companies. They share the vision we have with Wasp, understand and care about the problem we are solving.

Besides Lunar and HV Capital, we are thrilled to welcome on board:

  • 468 Capital (led by Florian Leibert, founder of Mesosphere and ex-Twitter and Airbnb eng.)
  • Charlie Songhurst
  • Tokyo Black
  • Acequia Capital
  • Abstraction Capital
  • Ben Tossell, founder of Makerpad (acq. by Zapier)
  • Muthukrishnan Ramabadran, Senior Software Engineer at Lyft
  • Yun-Fang, ex-Facebook engineer
  • Marcel P. Lima from Heller House
  • Chris Schagen, former CMO on Contentful
  • Rahul Thathoo, Sr. Eng. Manager at Square
  • Preetha Parthasarathy
  • John Kobs

Why did we raise funding?#

At its core, Wasp is an open-source project and we have full intention for it to stay that way. Open-source is one of the most powerful ways to write software and we want to make sure Wasp is freely accessible to every developer.

Wasp is a technically innovative and challenging project. Even though we are not building a new general programming language from scratch, there still exists an essential complexity of building a language and all the tooling around it. Wasp offers a lot of abstractions that are being introduced for the first time and there is no clear blueprint to follow, and this is why such an undertaking requires full-time attention and dedication. Hence, we plan on expanding the team with some amazing engineers to accelerate us on our journey.

Where are we today?#

Today, Wasp is in Alpha. That means there are many features we still have to add and many that are probably going to change. But it also means you can try it out, build a full-stack web app and see what it is all about. You can also join our community and share your feedback and experience with us - we'd be happy to hear from you!

Since we launched our Alpha several months ago, we got some amazing feedback on Product Hunt and Hacker News.

We've also grown a lot and recently passed 1,000 stars on our Github repo - thank you!

Wasp GitHub Stars

To date, over 250 projects have been created with Wasp in the last couple of months and some were even deployed to production - like Farnance that ended up being a hackathon winner! Check out their source code here.

Farnance screenshot

The team#

Martin and I have been working on Wasp for the last two years and together with our amazing contributors, who made us believe our vision is possible and made it what it is today. Having led development of several complex web apps in the past and continuously switching to the latest stack, we felt the pain and could also clearly see the patterns that we felt were mature and common enough to be worth extracting into a simpler, higher-level language.

The team
Martin and I during our first YC interview. Read here for more details on our journey to YC!

In case you couldn't tell from the photo and our identical glasses, we are twins (but not fraternal ones, and I'm a couple of minutes older, which makes me CEO :D)!

We are coming from the background of C++, algorithm competitions and applied algorithms in bioinformatics (Martin built edlib, his first OSS project - a popular sequence alignment library used by top bioinfo companies like PacBio) and did our internships in Google and Palantir. There we first encountered the modern web stack and went on to lead development of web platforms in fintech and bioinformatics space. We also had a startup previously (TalkBook), where we learned a lot about talking to users and building something that solves a problem they have.

What comes next?#

With the funding secured, we can now fully focus on developing Wasp and the ecosystem around it. We can start planning for more long-term features that we couldn't fully commit to until now, and we can expand our team to move faster and bring more great people on board with new perspectives and enable them to fully employ their knowledge and creativity without any distractions.

Our immediate focus is to bring Wasp to Beta and then 1.0 (see our high-level roadmap here), while also building a strong foundation for our open source community. We believe community is the key to the success for Wasp and we will do everything in our power to make sure everybody feels welcome and has a fun and rewarding experience both building apps and contributing to the project. If you want to shape how millions of engineers develop the web apps of tomorrow, join our community and work with us!

Thank you for reading - we can't wait to see what you will build!

We are in Alpha (try it out)!Join our communityWork with us

· 9 min read

You might have seen forall being used in Haskell like this:

f :: forall a. [a] -> [a]f xs = ys ++ ys  where ys :: [a]        ys = reverse xs

or

liftPair :: (forall x. x -> f x) -> (a, b) -> (f a, f b)

or

data Showable = forall s. (Show s) => Showable s

forall is something called "type quantifier", and it gives extra meaning to polymorphic type signatures (e.g. :: a, :: a -> b, :: a -> Int, ...).

While normaly forall plays a role of the "universal quantifier", it can also play a role of the "existential quantifier" (depends on the situation).

What does all this mean and how can forall be used in Haskell? Read on to find out!

NOTE: we assume you are comfortable with basic polymorphism in Haskell.

Quick math/logic reminder#

In mathematical logic, we have

  • universal quantifier
    • symbol: ∀x
    • interpretation: "for all", "given any"
    • example: ∀x P(x) means "for all x predicate P(x) is true".
  • existential quantifier
    • symbol: ∃x
    • interpretation: "there exists", "there is at least one", "for some"
    • example: ∃x P(x) means "there is some x for which predicate P(x) is true".

Vanilla Haskell (no extensions)#

In Haskell, all polymorphic type signatures are considered to be implicitly prefixed with forall.

Therefore, if you have

f :: a -> ag :: a -> (a -> b) -> b

it is really the same as

f :: forall a. a -> ag :: forall a b. a -> (a -> b) -> b

What forall here does is play the role of universal quantifier. For function f, it means it is saying "for all types, this function takes that type and returns the same type.". Other way to put it would be "this funtion can be called with value of any type as its first argument, and it will return the value of that same type".

Since forall is already implicit, writing it explicitly doesn't really do anything!

Not only that, but without any extensions, you can't even write forall explicitly, you will get a syntax error, since forall is not a keyword in Haskell.

So what is the purpose of forall then? Well, obviously to be used with extensions :)!

The simplest extension is ExplicitForAll, which allows you to explicitly write forall (as we did above). This is not useful on its own though, since as we said above, explicitly writing forall doesn't change anything, it was already implicitly there.

However, there are other extensions that make use of forall keyword, like: ScopedTypeVariables, RankNTypes, ExistentialQuantification. All these extensions automatically enable ExplicitForAll extension, which means you don't need to enable it yourself when using any of these. There is also TypeApplications extension which interacts with forall and in that case you might want to use ExplicitForAll with it.

Since forall is useful only when used with extensions, let's take a look at how it is used in each one of those!

forall and extension ScopedTypeVariables#

ScopedTypeVariables enables lexical scoping of type variables by explicitly introducing them with forall.

Let's take a look at the following example:

f :: [a] -> [a]f xs = ys ++ ys  where ys :: [a]        ys = reverse xs

This code does not compile, because compiler can't match type of ys with the return type of f. Why though, when they are both [a]? Well, that is because that is not the same a! Try changing ys :: [a] to ys :: [b] and you will get the exact same error, because it is exactly the same code -> a in ys :: [a] and a in f :: [a] -> [a] are different as and there is no connection between them. a in ys :: [a] stands for "any type", not for "that type that is reffered to with a in the type signature above".

This is where ScopedTypeVariables comes in:

{-# LANGUAGE ScopedTypeVariables #-}
f :: forall a. [a] -> [a]f xs = ys ++ ys  where ys :: [a]        ys = reverse xs

forall now gets special powers: the type variables bound by a forall (in our case a) scope over the entire definition of the accompanying value declaration (in this case definition of f).

This means that any mention of type a in the definition of f now refers to that a from the type signature of f, which is exactly what we needed, and the code example above now compiles.

forall and extension RankNTypes#

Basically, what RankNTypes does is enable you to use forall nested in type signatures, so that it does not apply to the whole type signature but just the part of it.

This enables some cool things that you were not able to do to before, for example you can specify that your function takes a polymorphic function as an argument.

Take a look at this example:

foo :: (forall a. a -> a) -> (Char, Bool)    -- We can do this only with RankNTypes.
bar :: forall a. ((a -> a) -> (Char, Bool))  -- This is usual stuff, we don't need RankNTypes for it. Actually we can even drop `forall` since it is implicit.

In foo, forall is applied only to the first argument of foo, which is a -> a, and not to the rest of the f's type signature. This can be done only with RankNTypes extension. bar on the other hand has forall applied to the whole signature, and we could have even ommited this forall since it would be there implicitly anyway.

Now, what does this mean? If we now have specificFunc :: Int -> Int and polymorphicFunc :: a -> a, foo polymorphicFunc will compile, while foo specificFunc will not! On the other hand both bar specificFunc and bar polymorphicFunc will compile.

This is because we specified, with forall, that foo needs a polymorphic function (function that takes value of any type and returns value of that same type) as the first argument, so we can't pass it a function like specificFunc that works only for Int -> such function is too specific. On the other hand, bar needs a function that takes value of some type and returns the value of that same type, so specificFunc is completely fine since it works only with Int, while polymorphicFunc is also ok although it is more general than what is needed, since compiler can easily specialize it.

Another example is liftPair function:

liftPair :: (forall x. x -> f x) -> (a, b) -> (f a, f b)liftPair func (y, z) = (func y, func z)
>> liftPair (:[]) (1, "a")([1], ["a"])

liftPair takes polymorphic function and applies it to both values in the pair. There would be no way to write its type signature without using RankNTypes.

forall and extension ExistentialQuantification#

ExistentialQuantification enables us to use forall in the type signature of data constructors.

This is useful because it enables us to define heterogeneous data types, which then allows us to store different types in a single data collection (which normally you can't do in Haskell, e.g. you can't have different types in a list).

For example, if we have

data Showable = forall s. (Show s) => Showable s

now we can do

someShowables :: [Showable]someShowables = [Showable "Hi", Showable 5, Showable (1, 2)]
printShowables :: [Showable] -> IO ()printShowables ss = mapM_ (\(Showable s) -> print s) ss
main :: IO ()main = printShowables someShowables

In this example this allowed us to create a heterogeneous list, but only thing we can do with the contents of it is show them.

What is interesting is that in this case, forall plays the role of an existential quantifier (therefore the name of extension, ExistentialQuantification), unlike the role of universal quantifier it normally plays.

GADTs#

Alternative approach to ExistentialQuantification is to use the GADTs extension, like this:

{-# LANGUAGE GADTs #-}data Showable where  Showable :: (Show s) => s -> Showable

In this case forall is not needed, as it is implicit.

forall and extension TypeApplications#

TypeApplications does not change how forall works like the extensions above do, but it does have an interesting interaction with forall, so we will mention it here.

TypeApplications allows you to specify values of types variables in a type.

For example, you can do show (read @Int "5") to specify that "5" should be interpreted as an Int. read has type signature :: Read a => String -> a, so what @Int does is say that that a in the type signature is Int. Therefore, read @Int :: String -> Int.

How does forall come into play here?

Well, if an identifier’s type signature does not include an explicit forall, the type variable arguments appear in the left-to-right order in which the variables appear in the type. So, foo :: Monad m => a b -> m (a c) will have its type variables ordered as m, a, b, c, and type applications will happen in that order: if we have foo @Maybe @Either, @Maybe will apply to m while @Either will apply to a. However, if you want to force a different order, for example a, b, c, m, so that @Maybe in foo @Maybe @Either applies to a, you can refactor the signature as foo :: forall a b c m. Monad m => a b -> m (a c), and now order of type variables in forall will be used when doing type applications!

This will require you to enable ExplicitForAll extension, if it is not already enabled.

Conclusion

This document should give a fair idea of how forall is used and what can be done with it, but it doesn't go into much depth or cover all of the ways forall is used in Haskell.

For more in-detail explanations and further investigation, here is a couple of useful resources:

This blog post originated from the notes I wrote in wasp-lang/haskell-handbook.

· 9 min read

Guest introducing themselves and getting full-access.
A Guest user getting access by introducing themselves in the "introductions" channel.

At Wasp, we have a Discord server for our community, where we talk with people interested in and using Wasp - Waspeteers!

In the beginning, we knew everybody in the community by their name, but as it started growing, we had a lot of people joining that never wrote anything, and the community started feeling less homey, less intimate.

This was when we decided to make it required for the new members to introduce themselves to gain access to the community. We knew that with this kind of barrier we would probably lose some potential new Waspeteers, but those that would go through it would be more engaged and better integrated.

We found no other way to accomplish this automatically but to implement our own Discord bot. In this post I will describe in detail how we did it.

· 7 min read

Wasp logo

For the last year and a half, my twin brother and I have been working on Wasp: a new programming language for developing full-stack web apps with less code.

Wasp is a simple declarative language that makes developing web apps easy while still allowing you to use the latest technologies like React, Node.js, and Prisma.

In this post, I will share with you why we believe Wasp could be a big thing for web development, how it works, where we are right now and what is the plan for the future!

· 6 min read

About a year or so ago, brother and I started discussing how awesome it would be to have a programming language that would understand what “web app” means. Such language would, on one hand, serve as an expressive specification of the web app, while on the other hand, it would take care of “boring” work for us, while we could focus on the business logic specific for our web app.

Step by step, the idea has started to take a more concrete shape, and Wasp (Web Application SPecification language) came to life! While still very early, we are writing this blog post to explain why are we building Wasp, what is the current status and what the future may hold.