In today's complicated web application ecosystem, starting a system from scratch is some seriously hard work. There's an incredible number of choices to make in terms of frameworks, JS language versions, build processes, back end integration, etc. And once you choose these things, you have to piece them together.
Fortunately, there's a number of different starter "seed" projects out on GitHub that have some pre-packaged solutions that have most of this work already done. You can generally find something that is built using the combination of things you're looking for, ready to go. However, these are almost exclusively *client-side* solutions. They provide everything you need for front end, but don't have any back end integration. For that you're on your own. This typically means you're running a development web server with your client, and then have to separately start up a back end - or mock it completely.
But there are a couple end to end solutions out there. For example, ServiceStack has some Visual Studio templates that come in multiple flavors and integrate with ServiceStack APIs. But I think there's a couple downsides to these. First, they are using only JSPM. Webpack is quickly becoming a standard and I would prefer to see them provide at least an option to use Webpack. Also, more importantly, you need to pay for ServiceStack licenses to use the backend technologies used in the templates. While ServiceStack is very powerful, it's also not cheap, and so it might not be an option for some teams.
That leaves the other full stack solution I've been able to find - the AspNET JavaScriptServices templates. These are pretty new, and have a lot of really cool features. They're using .NET Core, so they are a great opportunity to use latest Microsoft technologies. They are well integrated with Webpack and take advantage of all its features, like HMR (hot module reloading). And they all can be launched from single CLI "dotnet run". The UI and the backend API launch simultaneously, from the same project. You can have integrated debugging, from front to back and don't need to open up and work with multiple projects. This is a really nice developer experience.
Until now, the JavaScriptServices templates have only come in Knockout, Angular, and React. But as of this past Friday, with the merge of a new PR, there is also support for Aurelia. And, in fact, I was the one who submitted it! While it's not the most amazing work (and Steve Sanderson at MS deserves credit for the final Webpack configuration), it was work that needed to be done - and was definitely worth doing.
This was the first time I've made an open source contribution, and it was a great experience. It's personally rewarding to know that you've made a contribution that others can now use. I actually wasn't looking for something to contribute to, but while I was reading about the JavaScriptServices templates, I looked through the Issues log and saw there was a request to add support for Aurelia, and that it was up for grabs. Since I had just gone through some work converting some HTML dashboards to Aurelia and Angular2, and was interested in working with the templates, I figured I might as well try it. I'm really glad that I did.
As I wrote previously, I think Aurelia is a really well designed framework, and also think that the JavaScriptServices templates provide a great way to develop SPA applications. Hopefully others who are interested in using Aurelia can find it useful as a starting point in projects of their own. There's still work to be done for adding HMR support in Aurelia, so there's opportunity to improve the current implementation. It looks like this might be available sometime at the end of this year, or start of next.
Finally, I have to comment on how much I like Microsoft's .NET Core environment and tooling. Their CLI interface gives developers a first class command line experience, which is something quite different from the "Visual Studio does everything approach" we've seen from Microsoft until now. That approach could result in some strange things, where the build in Visual Studio was very similar, but not quite the same as the MSBuild you'd run as part of an automated process.
Now, you can easily work with the CLI build, run, and publish commands directly. For developers who are used to working with NPM at the command line for their JavaScript projects, this naturally becomes part of their development process. I used VS Code (which I also like very much) to do all the editing for both front and back end code, and relied solely on the CLI for doing all the project related tasks.
So, what are you waiting for, go download the JavaScriptServices Aurelia template and try it out!
Saturday, October 29, 2016
Sunday, October 16, 2016
Angular 2 and Aurelia, into the app code
In my last post, I discussed my attempts to convert the Gentelella Dashboard into React, Angular2 and Aurelia apps. There were different levels of success, but in the end I concluded that Aurelia had offered advantages over Angular2 (which was a little more difficult to work with) and React (which was impossible to implement the Dashboard without a re-write).
In this post, I'd like to get into a few more specifics, with examples. I'll walk through the application structure starting with the bootstrap and ending at the top level component. While I'm not going into anything complex here, I think it's nice high level view on the differences in structure between the two frameworks.
Bootstrapping
First, when implementing either Aurelia, or Angular2, you need to bootstrap the application into a web page. In the angular and Aurelia versions, the bootstrapping files are of similar complexity/structure. Both do some configuration, and then move to the next step in the process.
Angular 2 Bootstrapping
The important thing here is pulling in a definition of the AppModule, which contains references to all the components being injected into the main application.
Aurelia Bootstrapping
For Aurelia, the key line above is the setRoot, which invokes the root component for Aurelia.
At this point they diverge. The angular App module is a larger construct that knows a lot about the various components that are going to be used in the application. This is an extra step in the load process between the bootstrap and the eventual top level component. On the other hand, the Aurelia App just goes straight to the top level component, and its router.
Angular 2 App Module
Some of this is for supporting HMR, but in general this is a big chunk of code. This module is a central host to the components that are getting loaded into the Angular application. When you add new controls, you need to come here and add reference to them. At the top of the file, you can see a number of components are first being imported, and then lower down they are also injected into the module itself. The next step in the load process is handled via the @NgModule attribute, which has a reference to the bootstrap component, which is loaded first.
In Aurelia, the bootstrapper is where Aurelia is configured, and there's no equivalent central module to this. The Aurelia bootstrap directly loads the app component which contains the top level router info. That handles routing for the app template that is loaded up initially. The difference is that the Angular2 component learns about its components from the App Module, whereas in Aurelia the HTML template does an import of the child components before it can refer to them. Aurelia moves that composition responsibility to the template side, while it is embedded into the Angular 2 application code.
App Components
The next step in loading the application is starting up the top level App component. As you can see below the components being used at top level for Angular 2 and Aurelia are actually quite similar.
Angular 2 App Component
The important thing to notice here is the dependency on the @Component attribute for defining core functionality for the component. The attribute for selector defines how the component can be referenced from html, and the templateURL defines the template that is linked to the component.
Aurelia App Component
With Aurelia there is no required attribute necessary to define the selector or the template. Instead both can be defaulted to be defined by convention. In this case, as the class name is "App", the template for the html is expected to be app.html, and the html tag will also be "app". Also, if a css style file was used for just this component it would be defined in a require from the HTML template, not in the script file.
One thing of note here is that this top level component directly contains the router for the component, whereas in the Angular 2 application the router is in a separate routes component. There's some differences in how these routers work, but that's a worthy of a completely separate discussion.
HTML Templates
The final files I'd like to compare are the top-level App template files. With these you can definitely see how Angular2 relies more on the script code files than Aurelia.
Angular 2 App HTML Template
In Angular 2, the top level HTML template is just a list of three custom components and the Angular 2 router outlet. They are all components that are defined elsewhere. While this is a very minimalistic approach from the template side, what is not obvious is where these custom tags are coming from. You first need to do a search for the selectors to find the component, and then track back from the component to the template if you want to see where are the behavior is coming from.
Aurelia App HTML Template
There's a bit more template code here in Aurelia. At the top are the require tags which point directly to the source for the custom tags. There's also then some extra div tags that are moved here from index.html because of some differences in the way Angular 2 and Aurelia load up, but after that you essentially have same 3 custom components, and the router view for Aurelia. So the main difference is that the template is more self-descriptive. With the require tags, there's no mystery where you're pulling these components from.
Finally, one thing I do want to note is the custom-footer reference. In this case, there was no behavior needed, so it is an HTML only component. For the Angular 2 version I needed to actually create and import the component script class into the App Module in order to use this custom footer.
Conclusion
Hopefully this write up can be helpful to understand the different design approaches taken by the Angular 2 and Aurelia frameworks. Aurelia in general has less boilerplate code, and eliminates need for extra steps you need to configure in Angular 2. I found this very powerful for rapid prototyping where I was just piecing together HTML templates to produce a mockup without needing any backing Typescript files. It was possible to incrementally build up the application, instead of doing more work up front just to things working.
In the end, despite their differences, I do find Aurelia and Angular 2 to be similar enough that it is relatively easy to move between the two. There's a lot of analogous behavior, and they both are approaching things from the same direction.
In this post, I'd like to get into a few more specifics, with examples. I'll walk through the application structure starting with the bootstrap and ending at the top level component. While I'm not going into anything complex here, I think it's nice high level view on the differences in structure between the two frameworks.
Bootstrapping
First, when implementing either Aurelia, or Angular2, you need to bootstrap the application into a web page. In the angular and Aurelia versions, the bootstrapping files are of similar complexity/structure. Both do some configuration, and then move to the next step in the process.
Angular 2 Bootstrapping
/* * Angular bootstraping */ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { decorateModuleRef } from './app/environment'; import { bootloader } from '@angularclass/hmr'; /* * App Module * our top level module that holds all of our components */ import { AppModule } from './app'; /* * Bootstrap our Angular app with a top level NgModule */ export function main(): Promise<any> { return platformBrowserDynamic() .bootstrapModule(AppModule) .then(decorateModuleRef) .catch(err => console.error(err)); } // needed for hmr // in prod this is replace for document ready bootloader(main);
The important thing here is pulling in a definition of the AppModule, which contains references to all the components being injected into the main application.
Aurelia Bootstrapping
import {Aurelia} from 'aurelia-framework'; // we want font-awesome to load as soon as possible to show the fa-spinner import 'font-awesome/css/font-awesome.css'; import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap'; import '../styles/styles.css'; // comment out if you don't want a Promise polyfill (remove also from webpack.config.js) import * as Bluebird from 'bluebird'; Bluebird.config({ warnings: false }); export async function configure(aurelia: Aurelia) { aurelia.use .standardConfiguration() .developmentLogging(); // Uncomment the line below to enable animation. // aurelia.use.plugin('aurelia-animator-css'); // if the css animator is enabled, add swap-order="after" to all router-view elements // Anyone wanting to use HTMLImports to load views, will need to install the following plugin. // aurelia.use.plugin('aurelia-html-import-template-loader') await aurelia.start(); aurelia.setRoot('app'); // if you would like your website to work offline (Service Worker), // install and enable the @easy-webpack/config-offline package in webpack.config.js and uncomment the following code: /* const offline = await System.import('offline-plugin/runtime'); offline.install(); */ }
For Aurelia, the key line above is the setRoot, which invokes the root component for Aurelia.
At this point they diverge. The angular App module is a larger construct that knows a lot about the various components that are going to be used in the application. This is an extra step in the load process between the bootstrap and the eventual top level component. On the other hand, the Aurelia App just goes straight to the top level component, and its router.
Angular 2 App Module
import { NgModule, ApplicationRef } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { RouterModule } from '@angular/router'; import { removeNgStyles, createNewHosts, createInputTransfer } from '@angularclass/hmr'; /* * Platform and Environment providers/directives/pipes */ import { ENV_PROVIDERS } from './environment'; import { ROUTES } from './app.routes'; // App is our top level component import { App } from './app.component'; import { APP_RESOLVER_PROVIDERS } from './app.resolver'; import { AppState, InteralStateType } from './app.service'; import { Home } from './home'; import { About } from './about'; import { Footer } from './controls/footer.component'; import { Sidebar } from './menu/sidebar.component'; import {FlotCmp} from './controls/network-activities.component' import { Content } from './content'; import { NoContent } from './no-content'; import { XLarge } from './home/x-large'; import { FontAwesomeDirective } from 'ng2-fontawesome'; // Application wide providers const APP_PROVIDERS = [ ...APP_RESOLVER_PROVIDERS, AppState ]; type StoreType = { state: InteralStateType, restoreInputValues: () => void, disposeOldHosts: () => void }; /** * `AppModule` is the main entry point into Angular2's bootstraping process */ @NgModule({ bootstrap: [ App ], declarations: [ App, About, Home, Content, Footer, Sidebar, FlotCmp, NoContent, XLarge, FontAwesomeDirective ], imports: [ // import Angular's modules BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(ROUTES, { useHash: true }) ], providers: [ // expose our Services and Providers into Angular's dependency injection ENV_PROVIDERS, APP_PROVIDERS ] }) export class AppModule { constructor(public appRef: ApplicationRef, public appState: AppState) {} hmrOnInit(store: StoreType) { if (!store || !store.state) return; console.log('HMR store', JSON.stringify(store, null, 2)); // set state this.appState._state = store.state; // set input values if ('restoreInputValues' in store) { let restoreInputValues = store.restoreInputValues; setTimeout(restoreInputValues); } this.appRef.tick(); delete store.state; delete store.restoreInputValues; } hmrOnDestroy(store: StoreType) { const cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement); // save state const state = this.appState._state; store.state = state; // recreate root elements store.disposeOldHosts = createNewHosts(cmpLocation); // save input values store.restoreInputValues = createInputTransfer(); // remove styles removeNgStyles(); } hmrAfterDestroy(store: StoreType) { // display new elements store.disposeOldHosts(); delete store.disposeOldHosts; } }
Some of this is for supporting HMR, but in general this is a big chunk of code. This module is a central host to the components that are getting loaded into the Angular application. When you add new controls, you need to come here and add reference to them. At the top of the file, you can see a number of components are first being imported, and then lower down they are also injected into the module itself. The next step in the load process is handled via the @NgModule attribute, which has a reference to the bootstrap component, which is loaded first.
In Aurelia, the bootstrapper is where Aurelia is configured, and there's no equivalent central module to this. The Aurelia bootstrap directly loads the app component which contains the top level router info. That handles routing for the app template that is loaded up initially. The difference is that the Angular2 component learns about its components from the App Module, whereas in Aurelia the HTML template does an import of the child components before it can refer to them. Aurelia moves that composition responsibility to the template side, while it is embedded into the Angular 2 application code.
App Components
The next step in loading the application is starting up the top level App component. As you can see below the components being used at top level for Angular 2 and Aurelia are actually quite similar.
Angular 2 App Component
/* * Angular 2 decorators and services */ import { Component, ViewEncapsulation } from '@angular/core'; import 'style!css!less!font-awesome-webpack/font-awesome-styles.loader!font-awesome-webpack/font-awesome.config.js'; import { AppState } from './app.service'; /* * App Component * Top Level Component */ @Component({ selector: 'app', encapsulation: ViewEncapsulation.None, styleUrls: [ './app.style.css' ], templateUrl: './app.component.html' }) export class App { constructor( public appState: AppState) { } ngOnInit() { console.log('Initial App State', this.appState.state); } }
The important thing to notice here is the dependency on the @Component attribute for defining core functionality for the component. The attribute for selector defines how the component can be referenced from html, and the templateURL defines the template that is linked to the component.
Aurelia App Component
import {Aurelia} from 'aurelia-framework'; import {Router, RouterConfiguration} from 'aurelia-router'; export class App { router: Router; configureRouter(config: RouterConfiguration, router: Router) { config.title = 'Aurelia'; config.map([ { route: ['', 'dashboard'], name: 'dashboard', moduleId: './content/dashboard', nav: true, title: 'Dashboard' }, { route: 'inbox', name: 'inbox', moduleId: './content/inbox', nav: true, title: 'Inbox' }, ]); this.router = router; } }
With Aurelia there is no required attribute necessary to define the selector or the template. Instead both can be defaulted to be defined by convention. In this case, as the class name is "App", the template for the html is expected to be app.html, and the html tag will also be "app"
HTML Templates
The final files I'd like to compare are the top-level App template files. With these you can definitely see how Angular2 relies more on the script code files than Aurelia.
Angular 2 App HTML Template
<side-bar></side-bar> <topnav-bar></topnav-bar> <router-outlet></router-outlet> <custom-footer></custom-footer>
In Angular 2, the top level HTML template is just a list of three custom components and the Angular 2 router outlet. They are all components that are defined elsewhere. While this is a very minimalistic approach from the template side, what is not obvious is where these custom tags are coming from. You first need to do a search for the selectors to find the component, and then track back from the component to the template if you want to see where are the behavior is coming from.
Aurelia App HTML Template
<template> <require from="./menu/topbar"></require> <require from="./menu/sidebar"></require> <require from="./controls/custom-footer.html"></require> <div class="container body"> <div class="main_container"> <sidebar router.bind="router"></sidebar> <topbar></topbar> <router-view></router-view> <custom-footer></custom-footer> </div> </div> </template>
There's a bit more template code here in Aurelia. At the top are the require tags which point directly to the source for the custom tags. There's also then some extra div tags that are moved here from index.html because of some differences in the way Angular 2 and Aurelia load up, but after that you essentially have same 3 custom components, and the router view for Aurelia. So the main difference is that the template is more self-descriptive. With the require tags, there's no mystery where you're pulling these components from.
Finally, one thing I do want to note is the custom-footer reference. In this case, there was no behavior needed, so it is an HTML only component. For the Angular 2 version I needed to actually create and import the component script class into the App Module in order to use this custom footer.
Conclusion
Hopefully this write up can be helpful to understand the different design approaches taken by the Angular 2 and Aurelia frameworks. Aurelia in general has less boilerplate code, and eliminates need for extra steps you need to configure in Angular 2. I found this very powerful for rapid prototyping where I was just piecing together HTML templates to produce a mockup without needing any backing Typescript files. It was possible to incrementally build up the application, instead of doing more work up front just to things working.
In the end, despite their differences, I do find Aurelia and Angular 2 to be similar enough that it is relatively easy to move between the two. There's a lot of analogous behavior, and they both are approaching things from the same direction.
Sunday, October 9, 2016
React, Angular2, and Aurelia implementations of the Gentelella Dashboard Template
For the start of a personal project I’m working on, I was needing to have a halfway decent looking web UI. Not having a lot of web UI design experience, as a first step I started searching for available bootstrap templates, which led me to a great site here.
I looked through them all, but found the first one in the list, Gentelella, is particularly attractive to my UI sensibilities. From a technical standpoint, Gentelella, and the rest of the UI’s from the site, are basically implemented as a combination of Bootstrap, JQuery, along with a brew of Javascript/CSS libraries.
For my work, I’m needing to have these elements embedded into a Javascript UI framework of some kind. Anyone familiar with modern Web UI development knows that there are a *lot* of frameworks, and seemingly a new “hot” one comes to the forefront every year. (There’s a great summary of most of the last decade here, and as it was written in 2015, is fittingly about React).
This year, with Angular 2 finally out of it’s long beta, and React continuing strong, there’s a battle for framework of the moment. But there’s also another newcomer, Aurelia, that is showing some promise. I decided it would be useful to try to implement Gentelella in each of these three frameworks - React, Angular 2, and Aurelia.
React
Anyone familiar with React knows that it has a unique approach to the problem of how to update the DOM. It has it’s own Virtual DOM that is kept up to date internally by the react framework, and then used to apply only changes back to the real DOM. This, combined with popular use of a state management library like Flux or Redux, makes it a fundamentally different way of writing web applications. It also, unfortunately, makes it quite difficult to work with anything that is written in existing technologies.
Anyway, I pulled down a seed project, and got going. And then about an hour later, I stopped. It took me about this long to realize, with the time I was willing to invest, that this was not going work. The first thing I ran into was problems in mapping css classes to the “className” equivalent in React. React has it’s own approach to encapsulating HTML, which includes moving class behavior into it’s own custom className variable that more closely matches the DOM. The idea being that in react you’re not really working with HTML - and instead are working more closely analogous to the DOM itself.
But this customized approach to HTML markup, and the fact that a lot of the built in logic in Gentelella is using JQuery to manipulate classes, led me to conclusion that this would need to be a complete rewrite. Generally, it seems this would be the best approach when trying to incorporate code with a dependency on JQuery into React - are you sure you can’t rewrite it?
Angular 2
Since I was not excited about the prospect of a complete rewrite, I moved on to Angular 2. As I briefly mentioned above regarding React, when starting development on any of these frameworks you generally begin with some form of seed project. There is no official seed project available from the Angular 2 team, and instead two community driven projects have become quite popular. The angular2-seed is based on a Gulp/SystemJs build, and the angular2-webpack-starter is using Webpack. The subject of build tools in JS frameworks is worthy of entire separate discussion, but I chose to go Webpack route, which seems to be gaining traction at the moment as de facto standard.
The end result of this work I’ve made available here. This is currently not a complete implementation of all the Gentelella functionality, as the majority of the the links are still not mapped into Angular 2 routes. However, at its core is a functional implementation using Angular 2 UI components, navigation, and events.
Aurelia
Finally, Aurelia is a new JS framework project, led by Rob Eisenberg, who worked for a time on the Angular 2 implementation. Aurelia is his own take on what a modern JS framework should be, and makes some different design choices than Angular 2. A major focus in Aurelia is using convention to help remove boilerplate code, and generally attempting as best as possible to follow standards. This can be seen quite clearly in the view template syntax. The end result of the whole approach is less code, which is easier to read.
Rob has an excellent review of where the various JS frameworks are in 2016 here. If you have an hour, and are interested in JS Frameworks, it’s definitely worth watching. While he admits to personal bias, I actually think he’s spot on in terms of where the various frameworks stack up. And my experience in these conversion projects has backed this up.
To start the Aurelia implementation, I was able to use one of the official Aurelia seed projects, in this case using the one based on Typescript and Webpack (here) to align as closely as possible to the Angular 2 version. Unlike Angular2 and React, Aurelia publishes its own seed projects, which is very helpful for people just starting out.
The current product is here. In general I was able to more quickly pull in existing pages, and needed to write less code to implement the equivalent pages in Aurelia. You can also see that on a number of the script files there’s actually no reference to Aurelia code at all. Finally, there was no need to import and inject all the controls into the core application, which is an extra step currently needed for Angular 2 which has no equivalent in Aurelia.
Conclusion
In general, I found Aurelia to be superior framework for building out the Gentelella UIs. It wasn't even possible to do in a reasonable amount of effort with React. And Aurelia resulted in less work and cleaner code than Angular 2. You could say as a framework Aurelia is unobtrusive and lets you do things with less work. I’m planning to write a more in depth comparison of the results of the work in a future post, where I will get into more specifics on the differences. For people who would like to start using one of these new frameworks, being able to see examples that are a little more involved than the basic examples typically provided is a useful thing.
The Gentelella example is a particularly interesting one, I think, because it involves trying to fit these frameworks into an existing application, instead of a greenfield project or doing a complete re-write, which is sometimes not something you’re wanting, or able, to do.
Saturday, October 8, 2016
Post One!
As I approach the middle of my fifth decade on the planet, I realize that if I intend to do some things I’ve wanted to do for a while, I better get at it. One of those things is, strangely enough, writing a blog about my various software-related interests and experiences.
In my years of heretofore dedicated lurking, I’ve learned so much from other people’s blogs, GitHub projects, and stack overflow answers, that I hope that what I share can be of at least some value to others in return. I expect most of my posts will be relatively mundane, and focused on my effectively narrow experience in "line-of-business" software, but I wanted an interesting name for the blog.
What’s in a Name?
What exactly does the name of the blog, “software defined universe”, mean? First, as we continue into the 21st century software is becoming the core of everything we do. Or, as it was famously said, software is eating the world. We can see it in many of the companies that have come to permeate the way we live our lives. At their core, Amazon, Uber, and Netflix are essentially software companies
But it's beyond that. The survival of pretty much any business is dependent on software that runs it. Digital Transformation has become a buzzword across the business world, and there's general acceptance of the idea that any company that doesn't drive innovation through software will inevitably be disrupted and lose share in the market.
In the broader scope, our technology are inseparable from the software that is driving it. Advances in hardware are driven by the software that runs it. Software runs the drones that will be transforming our skies. Software runs the robots that operate our factories. And software drives the AI's that will increasingly handle many tasks currently done by humans.
It's all Information
The core of our dependence on software can be traced back to Information Theory, and leads to the present where increasingly we treat everything as information. Taken to its logical extreme, the fundamental thing to the universe isn't matter, it's information. Basically, there nothing that can't be broken down into a stream of 1's and 0's. And the thing that processes those 1's and 0's is software.
So after software finishes gobbling up the world, what next? The logical conclusion is that, eventually, it will eat the universe. It doesn’t even have to be us taking our software to the stars (or maybe the other way around). You can be certain that if there are technologically advanced aliens somewhere out there, they already have some pretty impressive software of their own. At some point they too would have realized how much easier it was to shop, find rides, and entertain themselves with the help of software.
The Software Defined Universe
But what if software doesn’t need to eat the world (or universe), because it already has? Recently, there’s been increasing discussion around the possibility of whether our universe actually *is* software. Perhaps, as initially popularized by The Matrix, our “reality” is just a very, very good computer simulation. The formal theory behind this, known as the Simulation Hypothesis, addresses the likelihood that this is true. Depending on assumptions one makes about advanced civilizations and their propensity for running simulations, it could be almost certain.
In the end, though, it doesn't really matter whether the universe is going to be taken over by software, or already *is* software. We have to live our lives the same, either way. But, I think it does make sense, while we are all here using - or running as - software, to spend some time talking (or in my case, writing) about it!
Subscribe to:
Posts (Atom)