I used Ruby to write a streaming app, a request-response app, a client-side web app, and a mobile app.
I'm Tristan, and I'm the Chief Problem Solver at Mindvalley. You can find me on facebook and github @parasquid,
But now, I love Ruby. Its currently my favorite language. It's the hammer I use to solve all the problems that come my way.
If I can just use Ruby all the time, not just for web but also on the frontend, and in mobile, I'll be happy.
Then I thought, hey that's a challenge! So I did, and that was the small project I made.
So that's it! It's proof that ruby is cross platform! We can now tell everyone we can also do mobile development, we just need to learn how to compile ruby using RubyMotion. Right? The thing is, I think the meaning of "cross platform" is usually misunderstood.
What does cross platform really mean then? In order to understand that, we need to know what cross platform isn’t. What does Voyager 2, your legacy Rails app, and your hackathon prototype have in common?
They're dead end products. When the Voyager 2 was made, they didn't intend to repair or update it after launch. If there was any glitch or malfunction, the only way to fix it was to launch another probe.
In some ways, it made the design process so much easier. The power cells can be directly welded to the modules because they'll never be taken apart anyway. So what if the antenna can only operate on a fixed frequency? There's no need for a tuning knob because it will never be retuned.
Then again, Voyager 2 will never change, and will never be used for a different purpose. You're not touching your legacy Rails app because you "don't fix it if it ain't broke," and you'd prefer to just rewrite it from scratch anyway. Your hackathon prototype works well for the pitch, but is not suited for production purposes.
It's the same with your code. Ask yourself the question: can you reuse a significant portion of the code you wrote, if by some magical means you can suddenly run your code in a different platform?
Cross platform means absolutely nothing if you can't reuse your code. I'll repeat this again, and you know it's important because I said it twice. Cross platform is useless if your code can't be reused. Interesting isn't it? What benefit is it that ruby runs on an iOS device when you have to rewrite the whole codebase from scratch anyway? You might as well just learn swift and do it the way apple wants you to do it.
Reuse of prior work across multiple platforms is the biggest reason why you want cross platform support.
So how do you design your code so that it is reusable?
Let's take a look at the calculator. Notice how the UI and the domain logic are separate. How in this example, we put the React UI, while in this example, we put the telnet UI and here, we put the iOS ui. It's almost as if you're just able to just swap things around just like that! Right? Do you know what this reminds me of?
This. A USB cable.
I can use the same cable to charge a cellphone, a powerbank, a camera, and various other things.
They all follow the same standard so they are all interchangeable. And I don't have to know which pin of the USB connects carries the electricity--I just know that if I plug one end to a power source and the other end to the device, it will charge.
Nobody would think of making a phone that has its power cable attached and not detachable. Well maybe Apple might.
in majority of the cases, the cross platform problem is solved in the physical world. We intuitively understand how to design products that can be reused. But because software is intangible it's difficult to apply our experience as meatbags to the act of creating software. So what can we take from designing physical products and apply it to software development?
Let's take a look at the USB cable example again.
Remember when I said they all follow the same standard? That means the USB cable can treat different objects in the same way. They all have the same USB interface, so from the point of view of the cable, they're all the same thing (even though they're not).
How about when I said that I need to care about the electromotive force or which pins carry the electricity? That means I don't even have to know how the cable was made in order to use it! I also immediately know that the cable is not for drying my hair, or for opening a can.
It was less complicated because the list of things I can do with the cable was small, and the kinds of things I can use the cable was also small.
In effect, you’re replacing the questions on the left with this one question on the right. You’re reducing the surface area of the complexity involved. I think some of you would already know where I’m going here.
Instead of asking how does this work, or which things can I use this with, I now just ask: what can this do?
Encapsulation: What vs How Polymorphism: What vs Which
These two might sound familiar. You'd know them from Object Oriented Programming.
But they’re not purely the domain of OOP
Here’s an example. what does a storage device, an input device, and a network socket have in common?
From the Unix perspective, they’re all files. Everything is a file
Well technically, everything is a file descriptor. But have you ever wondered how is this possible? How can you have an operating system treating everything as if it’s a file?
There are generally five functions you need to implement if you want to write a device driver for a Unix system. open close read write seek If you implement these (and note that returning nil is an implementation; just take a look at /dev/null) then you conform to the file descriptor POSIX API specification.
That also means you get the benefits of getting treated just like every other file. Or, just like what Tyler Durden from fight club says: you are not a beautiful or unique snowflake. And that’s a good thing!
That means that you can use common tools to operate on different things. Whether it’s a storage device, a network stream, or the keyboard circular buffer, you can use the common unix tools to operate on them even if you don’t care about whether the device responds — just use /dev/null
in this case, common tools operating on different things is the principle, polymorphism is the technique
you can also have encapsulation without oop in fact, you’re using it all the time. when you open your browser and go to google.com you don’t really care about how google retrieved the search results; all you’re interested in is what the search results are.
in a sense, your only public interface is that of the search bar. it represents the big, complicated machinery that is called google search, and presents it as something that can be easily understood.
A more code related example: Let’s take a quick look at the calculator brain. Notice how the Brain class only has a few exposed methods, out of which two are used in react: display, input_char. Three if you count new which creates an object out of the class.
Here’s the object that have its methods exposed. This isn’t the best implementation of a calculator, but that’s okay. Notice that I can easily refactor or rewrite this entirely without severely affecting the UI framework. Because the UI framework only know about a few very specific api calls, they don’t know a lot about how the brain operates. They are shielded from changes in the brain.
That’s because this object has a very small surface area. Note the distinction here. I didn’t say that the object was small - it really isn't. I said that its surface area is small. If you've ever encountered God objects in your code (hint: check all files in your code that end with "service") then you know the problem very well. You have a very hard time debugging your application because it's just so big and complex!
Well. You see, the problem isn't that those objects are too big.
It’s because God objects are too fat. When your objects try to expose a lot of methods in its interface, the API surface area of your objects is too wide. And that makes your app complex and difficult to manage because these APIs will get used, and you need to remember all of these things. What connects to where?
so the principle here is information hiding, and the technique is encapsulation. this concept allows you to represent something really big and complex with something small and simple by exposing a small surface api.
But yeah, we all know about this already, right? Ruby is OOP, we use OOP all the time! Here's the thing: If you ask many programmers what OOP is about, they’ll just tell you it’s using objects to do stuff.
They’ll tell you that OOP is about programming with objects. It’s not, that’s just a tautology--saying the same thing twice in different words.
So no. It’s not just about programming with objects.
It’s a different way of thinking about your code, where you tell an object what you want, instead of asking data from it. It is a method of organization. And it’s just one of many.
Just like Functional, Prototype-based, Rails-way based, or a combination of these are. They are all ways to organize your program so you can more easily figure out how things are connected.
I think we're doing ourselves a disservice by not going back to basics and experiencing for ourselves a whole new different way of writing our apps.
If you want to write cross platform apps, you need to write reusable code. Unfortunately, the Rails way of writing apps is not enough.
That doesn’t mean that the Rails way is “wrong” or that we should stop using it because it “promotes bad habits” a framework is, after all, just a tool and it is up to the programmer to decide how to use it.
DHH famously said that Rails is Omakase, that he is the head chef that decides the experience that is Rails. And this arrangement has been great because Rails is an awesome framework that makes it really easy to make web applications. And that is where we run into a problem with cross platform support; the Rails way is too web centric that it’s very difficult to reuse prior work for other platforms.
Does that mean we should just not use Rails or any other framework at all? Not at all.
This is Kent Beck, he is the founder of Extreme Programming, the precursor to what we now call agile software development, and from where we get the scrum methodology. One of his papers talked about connected and modular designs, and the Rails way falls quite near to the connected design model. Here’s what he said:
This is one of the reasons why Rails apps can be built so fast. I join hackathons, and I always use Rails because it’s really so easy to just build features because I have access to everything. It’s one of the reasons why bootcamps and one day tutorials can show people the power of programming—because producing a usable output is so easy.
Here’s the next thing he said:
and so we have a problem. A mature system benefits from a modular design. We know about the factory factory jokes in java, but the reason why there’s so much code monkeys in enterprise application development is because the modular design allows easy distribution of work. but it’s so costly to start modular
The advice then, is not to ditch connected designs. Kent Beck recommends to stay on the connected curve until the climb phase, then switch to the modular curve.
How do you know when the climb phase begins? That’s when experience comes in. How would you even know what to switch to if you’re not familiar with how modular designs look like?
We need to find that knowledge to organize your code as if it was a USB cable. That would be really cool. If you wanted to swap the UI, all you needed to do is to write the UI layer for that framework.
The rails way is what we're familiar with. Many of us started learning ruby because of rails. I got reintroduced to ruby because Mindvalley uses rails.
But my point is, that it’s not the only code-organization technique out there. And we haven’t been seeking them out. Have you tried any of these? Have you tried to at least deviate a little bit from the Rails way? Here you have a command-query separation by Bertrand Meyer, here you have event sourcing which says that all changes to an application state should be stored as a sequence of events, and here we have the DCI paradigm which builds on top of OOP.
Our industry is still young, but is now mature enough to recognize that there are many different ways to skin a cat. These are just a few presentations tackling a different face of the same issue: we are too web centric because the Rails way makes it easy to write web applications
Tightly coupling with the rails framework works great if you want to create apps that are similar to Basecamp, you’re targeting web only, and you know that's the final iteration of your product. But sometimes, your app is far out different from basecamp. If you are planning to reuse a significant portion of your code across platforms, the rails way is not enough. But more importantly, it’s not the only organizational technique available to you.
I think that as programmers, we need to start looking beyond what we’re comfortable with, and start rediscovering solutions to problems that have been solved by other disciplines.
But if you had only one idea to take home with you, let it be this: that programming is fundamentally an activity by humans, for humans. Many people think that programming equals coding, and that’s really disappointing. That’s just scratching the surface, because programming is more than that.
Programming is the act of managing complexity.
We often make the mistake of thinking that we're programming for the computer. No, your computer won't run your program faster solely because you used a well designed architecture. But YOU will be faster, because your wonderful but still limited brain can now comprehend the relationships in the code.
By organizing our code and designing it so it’s easy to understand, we free our brains and give it space to think about the stuff that really matters: what is your app supposed to do?
When you start focusing on that question, and start expanding your toolbox, you end up with more flexible, maintainable, and reusable code. And that’s it. That’s the answer. That's how you write cross platform apps.
I'm Tristan, and I'm the Chief Problem Solver at Mindvalley. You can find me on facebook and github @parasquid
Life Beyond Rails: Creating Cross Platform Ruby Apps
Life Beyond Rails
Building Cross Platform Applications
So what if voyager can go to
Mars, or dive in the ocean
So what if your hackathon was
written in JS that runs on both
server and client
So what if your legacy code was
written in Ruby (cross platform!)
Can you reuse a
significant portion of the
code you wrote?
Cross platform is useless
if your code can't be
Reuse of prior work across
multiple platforms is the biggest
reason why you want cross
“cross platform” is mostly
solved in the physical
• How does this work?
• Which things can I use this
• What can this do?
• How does this work?
• Which things can I use this
• What can this do?
In a connected system, elements are highly
available to each other (via global state, for
example). Adding the first feature to a
connected system is cheap. All the resources
you need are available. However, the cost of all
those connections is that subsequent features
are very likely to interact with previous features,
driving up the cost of development over time.
A modular design has connections deliberately
kept to a minimum. The cost for the first feature
is likely to be higher than in the connected
system, because you need to find the necessary
resources and bring them together, possibly re-
modularizing in the process. Features are much
less likely to interact in a modular system,
though, leading to a steady stream of features at
relatively constant cost.
Stay on the connected curve until the climb
phase, then switch to the modular curve.