I Should Write Bad Code More Often

I Should Write Bad Code More Often

I’ve spent most of my career writing bad code – code that is ugly, hard to read, hard to understand, darn near impossible to update and fix problem. It’s bad code, not because it didn’t satisfy the original needs, but that it did so in a way that was not maintainable.

Most people (myself included) look at the code they just wrote and think about how great it is; how it’s so easy to read and understand, and how they will be able to use this code to do so many things. The thing that we forget, is that we are always looking at the code we just wrote through the eyes of context and short term memory. Of course I understand how that function works – I just wrote it. But in a few months, weeks, days… sometimes hours? Well…

For most of my career, though, I’ve had this lofty ideal of writing great code all the time. Worse yet, I’ve had this delusion that my code is somehow better than the code that I was reading from other people. But as I continue to grow and learn, in invariably look back at my old code and think about how bad it is. I’ve come to realize – albeit slowly, over many years – that this is ok for a couple of reasons.

You’re Learning
Most of the bad code that I write is because I’m learning – learning a new language, a new framework, a new tool, a new API, a new … something. When we’re learning, we don’t know the answers yet. That’s why it’s learning – to find the answers.

To find the answers, though, we have to solve the problem at least once – and there’s a good chance that the code we write will be bad, the first time we solve it. We can’t be concerned with good abstraction, clean code, elegance, simplicity or maintainability when we’re in learning mode. We have to focus on succinct, to the point, solve the immediate problem styles of coding with little to no regard for 10 minutes from now, let alone 10 days from now. This is the nature of learning.

You’re Ready To Learn
If you know you’re writing bad code, then you’re already one step ahead of the game. Knowing that the code you’re writing is a sign that you know you can improve. It’s a sign that you want to improve. It’s also a sign that you are ready (or almost ready) to take the next steps to improve.

Recognizing your own code as bad code is a sign of learning. That code you wrote 3 months ago, which you are now embarrassed to look at? That’s a good sign. It means you’ve learned since then. The code you’re writing for your current project, which keeps giving you bad feelings and making you nervous? At least that is recognition of the need to learn.

Recognizing your own code as bad is one of the best things you can do for yourself, you projects and your career because it means you want to learn and improve.

The Learning Experience
The real problem with bad code isn’t the code itself. The real problem stems from confusing the learning experience with solving problems for production systems. Far too often, we allow code that we know is bad to become production code. Sometimes this can’t be helped. Sometimes it isn’t a bad thing, either. But most of the time, the bad code that we wrote while learning shouldn’t be put in production.

Learning should be done in throw away projects, samples and demos. Rather than trying to learn how to use that library in your production code, build a demo app that mimics your production needs (to a minimum). Instead of deciding to use a new framework for the first time when starting a new project, take some time to play with it first. Dig in a little deeper than you think you need to, and try to understand how it works and why.

Separating the learning process from the production code process is important. Taking the time to learn how to solve a problem will produce better code and better understanding of the problem when you get back to your production system. You’ll be better equipped to deal with edge cases, additional requirements and other issues that don’t show up the first time you solve sometime.

Write Bad Code
You may not always have control over timelines. You might not have time to write a sample project for every idea., and sometimes you don’t have a choice but to release bad code. This is the reality in which we live. But I encourage you to to try, whenever possible, to write bad code knowing that you will be throwing it away once you start working on the production-ready solution.

5D Co-working Lab

5D Co-working Lab

5D Co-working Lab @ Development Innovations is a co-working space focused on connecting civil society organizations with local service providers, freelancers, and consultants. The Lab also hosts the Development Innovations advisory service, which provides resources and training for organizations that want assistance to develop their technology-enabled ideas or projects.

5D Co-working Lab

Address: #296, St. 271, Sangkat Toul Tompong 2, Khan Chamkarmorn, Phnom Penh, Cambodia
Hours: Mon-Fri (8:00am - 5:30pm)
Parking: Street and Parking Lot
Phone: +855 23 266 271

Emerald HUB

Emerald HUB

Emerald HUB is a Cambodia Co-Working space, located in Phnom Penh city. Emerald HUB offers young startup, entrepreneurs, small and growing businesses, service providers and investors the opportunity to work in an innovative, energetic and collaborative work space. Emerald HUB members can work at shared and dedicated “desks”, dedicated offices or private rooms, complimented with high speed Wi-Fi Internet and relevant amenities.

Emerald HUB

Emerald HUB

Emerald HUB

Emerald HUB

Address: St. 169, Phnom Penh, Cambodia 12253
Hours: Mon-Sun (8:00am - 9:00pm)
Parking: Street and Parking Lot
Phone: +855 97 492 7777

One Thing Cambodian Startups Need to Learn English

One Thing Cambodian Startups Need to Learn English

Being a startup in Cambodia is a particularly existential experience. You have to constantly evaluate what your place is in the society, consider whether or not your idea works, evaluate yourself as a leader, and fight for your own reason to exist. On top of that, you have to look at where you need to improve. I’m always meeting young startups who are asking me “What should I learn? Where should I go? What problems can I solve?”

English is really important.

5 Reasons Why English is Vital to Your Startup

The key with English for Cambodian startups is access. If you cannot understand, read, and converse with foreigners, you will not get the full benefit of the interaction. Here’s five nuanced examples of why English is so important for startups to win in the next ten years.

1. English at Events in Cambodia
Although there was simultaneous audio translation with headphones, there is just no way that you can catch all the nuances and understand in the translation. Some of my colleagues at the event only understood 70 percent of what translator said because there weren’t enough headphones. Even with headphones, people could only understand so much. translator spoke so fast and with idioms that the professional translators could scarcely catch up to. Now 70 percent, you may think, sounds like they understood a lot. But that has exponential consequences. If you only understand 70 percent, and you go up to talk to the speaker after the event, and you ask him a question he already answered, he might think you weren’t listening. He might be unimpressed. But if you could understand 100 percent, you could take the conversation to the next level. That will impress the speaker, and impressing the speaker will bring enormous benefits in the long run. If you understand only 70 percent, what happened to that 30 percent? In that 30 percent could have been a piece of advice that would have deeply helped your startup.

In Phnom Penh, a large influx of foreign interest and foreigners come to Cambodia. Some are successful entrepreneurs, some are startups, some are just good possible links into global ecosystems. If your English sucks, how are you going to talk to them?

2. English at Events Outside Cambodia
If you’re thinking about going global or getting your name out there, you’re bound to go to an event outside of Cambodia. At that time, if you’re a founder, and you can’t communicate with foreigners, you’ve wasted money on a plane ticket. At a recent event I went to outside of Cambodia, one of the Cambodian founders I was hanging out with couldn’t understand a simple question from a Singaporean investor. I had to help translate the question for him into Khmer and then back into English. What would happen if I wasn’t there? These situations are lost opportunities. Rubbing elbows with other people in English is a skill you cannot underestimate. It could lead you down the path to better networks, and possibly even investment. Bad english makes you look like an idiot. Don’t look like an idiot.

3. Getting Knowledge Online
The plethora of information about technology and startups online is vast. For the techies, StackOverflow is the place to be. For people looking to understand the personal and intimate mechanics of certain tech companies and startups, Quora is the place to be. I haven’t even begun to mention the pulse of news that you can find on TechCrunch, PandoDaily, Tech In Asia, e27, Cnet, GigaOm, and more. All the best up-to-date information out there is in English. Also, all the best advice on technology and startups is laid out across personal and company blogs across the internet, in English. If you don’t know English, you might be building a project on top of technology that is outdated, or employing techniques and trends that just don’t work anymore. If you want to stay ahead of the game, English is the way.

4. Interacting with Foreigners Online
And it’s not just knowledge and news that’s online. People are also online. If you can’t write an email that sounds like you are a professional, passionate, and eloquent person, why would anyone reply to you? This is also true on Twitter, Facebook, and any other platform that involves interacting with other people on a professional and casual level.

5. Pitching Your Product
Probably the most important and last point in this mix is pitching your product. It is not easy to set up your startup or product in one minute. Brevity is a skill of great communicators. Tell me what your product is, in English, in 30 seconds. Now elaborate on it for the next hour, while I ask you penetrating questions about how the business works. Can you do it? For your sake, I hope so.

At the end of the day, if you’re playing to win – and you should be if you’re doing a startup – you should know English. It doesn’t matter if you are competing on a global or domestic level. Either way, you have to be top class. And top class people know English. Besides the benefits above, being bilingual gives you mental dexterity and diversity. People who know multiple languages are generally more open to new ideas and have more reflection on their own culture. These are essential advantages for a young startup in the battlefield that is Cambodia.

It is true that all the above applies for startups across the Asian startup ecosystem. But it is presently a huge obstacle that Cambodian startups need to pay special attention to, since many view Cambodia as a blackbox, a startup world that is hard to understand and wrap one’s head around. Especially if Cambodia wants to be known outside of Cambodia.

Rails Model View Controller

Rails Model View Controller

The browser makes a request, such as http://geekhmer.com/video/show/15

The web server (mongrel, WEBrick, etc.) receives the request. It uses routes to find out which controller to use: the default route pattern is /controller/action/id as defined in config/routes.rb. In our case, it’s the “video” controller, method “show”, id “15”. The web server then uses the dispatcher to create a new controller, call the action and pass the parameters.

Controllers do the work of parsing user requests, data submissions, cookies, sessions and the “browser stuff”. They’re the pointy-haired manager that orders employees around. The best controller is Dilbert-esque: It gives orders without knowing (or caring) how it gets done. In our case, the show method in the video controller knows it needs to lookup a video. It asks the model to get video 15, and will eventually display it to the user.

Models are Ruby classes. They talk to the database, store and validate data, perform the business logic and otherwise do the heavy lifting. They’re the chubby guy in the back room crunching the numbers. In this case, the model retrieves video 15 from the database.

Views are what the user sees: HTML, CSS, XML, Javascript, JSON. They’re the sales rep putting up flyers and collecting surveys, at the manager’s direction. Views are merely puppets reading what the controller gives them. They don’t know what happens in the back room. In our example, the controller gives video 15 to the “show” view. The show view generates the HTML: divs, tables, text, descriptions, footers, etc.

The controller returns the response body (HTML, XML, etc.) & metadata (caching headers, redirects) to the server. The server combines the raw data into a proper HTTP response and sends it to the user.

It’s more fun to imagine a story with “fat model, skinny controller” instead of a sterile “3-tiered architecture”. Models do the grunt work, views are the happy face, and controllers are the masterminds behind it all.

Many MVC discussions ignore the role of the web server. However, it’s important to mention how the controller magically gets created and passed user information. The web server is the invisible gateway, shuttling data back and forth: users never interact with the controller directly.

So far so good, That’s it!!! See ya!!! :)

How to Make Project Success on GitHub?

How to Make Project Success on GitHub?

Community-driven software development is becoming the new path to brand and project success. You would be advised to pay attention to this surging trend: With more than 27 million projects currently on GitHub, the ever-increasing popularity of community-driven development is evident.

I’ve come up with five concrete ways you too make your project successful on GitHub. It’s all about engaging the community.

1. Prioritize Your Community
Open source is more than a license and software development model; it’s also largely about the people. Encourage both users and maintainers to collaborate to promote a surge in new ideas. You’ll find that most prominent projects incorporate a community of contributors with a mailing list, GitHub project.

2. Welcome New Contributors
Contributors often feel intimidated and self-conscious when starting a new project. Help them break out of their shell by offering a set of contribution guidelines and using an issue tracker to highlight troubleshooting that can be easily handled by new contributors. Laying out the proper groundwork is vital to the success of your project.

3. Appreciate Contributors’ Work
All those who contribute are co-creators and have ownership over their work on each project. we recognize each contributor’s input in a variety of ways. Easy things to do that go a long way are shout-outs, a kind note, or sending some swag, like a T-shirt.

4. Use Art to Visualize Your Ideas
Art has a powerful capability to communicate ideas, just like software. Take advantage of graphic design to brand your projects. Consider creating a friendly mascot (like the GitHub Octocat) to help distinguish your project for your community. In addition, use art to illustrate and teach complex ideas and instructions. By incorporating art into your project, you may also encourage artistic contributions from your new contributors.

5. Implement Documentation to Prevent Problems
Fastidious recording of your project process can avert potential issues and confusion. Good documentation includes a FAQ section, a set of instructions for common patterns, and a set of examples. You can even make your documentation open source by asking for contributions from those working on your projects.

So far so good, Accelerate engaging a community of volunteer software developers by making them feel welcome, giving them clear tasks, and establishing tangible ways for them to be completed. Most importantly, thank contributors when they help you. Your project will flourish because of it. See ya!!!

How to Make Session Data Available to Models in Ruby on Rails?

How to Make Session Data Available to Models in Ruby on Rails?

Ruby on Rails is implemented as the Model View Controller (MVC) pattern. This pattern separates the context of the Web Application (in the Controller and the View) from the core Model of the application. The Model contains the Domain objects which encapsulate business logic, data retrieval, etc. The View displays information to the user and allows them to provide input to the application. The Controller handles the interactions between the View and the Model.

This separation is a very good design principle that generally helps prevent spaghetti code. Sometimes though the separation might break down.

Rails provides the Active Record Callbacks which allows you to write code that will respond to the lifecycle events of the Model objects. For example you could log information every time a specific kind of Model object is saved. For example you could record some information every time an Account changed using the following:

account.rb
1
2
3
4
5
6
7
8
class Account < ActiveRecord::Base
  after_update :log_audit_change

  private
  def log_audit_change
    Audit.audit_change(self.id, self.new_balance)
  end
end

You might have noticed a limitation with the previous API though. You didn’t notice? The only information passed to the model is the Object that is being changed. What if you want more context than this? For example, what if you want to audit not only the values that changed them, but the user who made the change?

account.rb
1
2
3
4
5
6
7
8
class Account < ActiveRecord::Base
  after_update :log_audit_change

  private
  def log_audit_change
    Audit.audit_change(current_user, self.id, self.new_balance)
  end
end

How do you get the current_user value? Well, you have to plan ahead a little bit. The User in this application is stored in the HTTP Session when the user is authenticated. The session isn’t directly available to the Model level so you have to figure out a way around this. One way to accomplish this is by using a named Thread local variable. Each HTTP request is served by its own thread. That means that a variable stored as thread local will be available for the entire processing of a request.

The UserInfo module encapsulates reading and writing the User object from/to the Thread local. This module can then be mixed in with other objects for easy access.

user_info.rb
1
2
3
4
5
6
7
8
9
module UserInfo
  def current_user
    Thread.current[:user]
  end

  def self.current_user=(user)
    Thread.current[:user] = user
  end
end

A before_filter set in the ApplicationController will be called before any action is called in any controller. You can take advantage of this to copy a value out of the HTTP session and set it in the Thread local:

user_info.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ApplicationController < ActionController::Base
  before_filter :set_user

  protected
  def authenticate
    unless session[:user]
      redirect_to :controller => "login"
      return false
    end
  end

  # Sets the current user into a named Thread location so that it can be accessed by models and observers
  def set_user
    UserInfo.current_user = session[:user]
  end
end

At any point in a Model class that you need to have access to those values you can just mixin the helper module and then use its methods to access the data. In this final example we mixin the UserInfo module to our model and it will now have access to the current_user method:

account.rb
1
2
3
4
5
6
7
8
9
10
class Account < ActiveRecord::Base
  include UserInfo

  after_update :log_audit_change

  private
  def log_audit_change
    Audit.audit_change(current_user, self.id, self.new_balance)
  end
end

You generally shouldn’t need this kind of trick outside of a model. In most cases the Controller should pass all of the information needed by a Model object to it through its methods. That will allow the Model objects to interact and the Controller to do the orchestration needed. But in a few special cases, this trick might be handy.

So far so good, That’s it!!! See ya!!! :)

Rails Tagging

Rails Tagging

For instance, in a social network, a user might have tags that are called skills, interests, sports, and more. This article will show you how to implement tagging in your Rails application. Let’s run through this with me.

Installation
Add gem acts-as-taggable-on to your Gemfile:

Gemfile
1
gem 'acts-as-taggable-on'

and run bundle:

1
bundle

install migrations:

1
2
rails generate acts_as_taggable_on:migration
rake db:migrate

This creates some tables and doesn’t need to know anything specific about your models. The models and concerns for working with these tables (Tag, Tagging, etc).

Model Integration

app/models/post.rb
1
2
3
class Post
  acts_as_taggable
end

Now you can do, eg:

1
2
3
4
post = Post.create
post.tag_list = "programming, ruby, rails"
post.tag_list
# => ['programming', 'ruby', 'rails']

#tag_list= takes a string and splits it up, using the resulting substrings to find_or_create Tags, and associate them with the taggable thing (eg, Post), through Taggings. You don’t need to know this.

The important thing is that, with #tag_list= we can manage tags via a comma-separated-list in a text field in a form.

Controller Integration

app/controllers/posts_controller.rb
1
2
3
4
5
6
7
8
9
10
class PostsController < ApplicationController
  def create
    @post = Post.create(post_params)
    redirect_to @post
  end

  def post_params
    params.require(:post).permit(:title, :body, :tag_list)
  end
end

This is assuming you’re trying to tag an existing Post model with title and body fields, web-accessible through an existing PostsController, with index, show, new, etc. methods.

Substitute your own, the important machinery here is whitelisting tag_list from the params.

Form Integration

app/views/posts/_form.html.erb
1
2
3
4
5
6
<%= form_for post do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body %>
  <%= f.text_field :tag_list %>
  <%= f.submit %>
<% end %>

Now you can create tags for stuff. How about displaying them? Couple options, I’ll go through the most explicit (a TagsController with index and show actions), but they can be rolled up into other controllers/actions.

Controller

config/routes.rb
1
2
3
4
Rails.application.routes.draw do
  resources :posts
  resources :tags, only: [:index, :show]
end
app/controllers/tags_controller.rb
1
2
3
4
5
6
7
8
9
10
class TagsController < ApplicationController
  def index
    @tags = ActsAsTaggableOn::Tag.all
  end

  def show
    @tag =  ActsAsTaggableOn::Tag.find(params[:id])
    @posts = Post.tagged_with(@tag.name)
  end
end

It’s unfortunate we have to do this slightly awkward workaround with Post.tagged_with(@tag.name) in tags#show. The ActsAsTaggableOn::Tag model does not have a built-in relationship with its taggable types (this is a necessary consequence of some polymorphism which we’re not using here). We could add one for Post, but this way is easier to demonstrate.

Tags Views

app/views/acts_as_taggable_on/tags/_tag.html.erb
1
<%= link_to tag.name, tag_path(tag) %>
app/views/tags/index.html.erb
1
2
<h1>Tags</h1>
<%= render @tags %>
app/views/tags/show.html.erb
1
2
<h1><%= @tag.name %></h1>
<div><%= render @posts %></div>

Note the partial path is acts_as_taggable_on/tags/tag. This is so we can just say render @tags and let rails do its implicit magic. There are other ways to organize everything, but this is the simplest.

View Integration

app/views/posts/_post.html.erb
1
2
<h2><%= link_to post.title, post_path(post) %></h2>
<div><%= render post.tags %></div>
app/views/posts/index.html.erb
1
2
<h1>Posts</h1>
<%= render @posts %>
app/views/posts/show.html.erb
1
2
3
<h1><%= @post.title %></h1>
<div><%= @post.body %></div>
<div><%= render @post.tags %></div>

And that should be it. It’ll look shitty, but I hope you can figure out how to elaborate on this once you have it working. If anything here is assuming familiarity with something you don’t have, ask and I will gladly elaborate.

So far so good, That’s it!!! See ya!!! :)

Rails 5 ActionCable Architecture and Concepts

Rails 5 ActionCable Architecture and Concepts

ActionCable is an component of Ruby on Rails 5.0 - it is the first “official” solution for integrating websocket communication with Rails.

Websocket Concepts
The basic protocol that is pervasive in browser server interaction is HTTP. This is the protocol that gets used when a browser asks for an HTML page, JavaScript or CSS assets.

An HTTP connection is typically short-lived, initiated by the browser and ends when the application server has returned a response. This poses a problem given an event that browsers need to know about, but have not asked for - the server has no way to send data to the browser unasked.

Before websockets, developers have opted for polling loops or long-running http requests (ActionController::Live) to solve such cases, both of which have their own technical difficulties.

Websockets enable browsers and application servers to keep an open connection, enabling both parties to initiate sending data to each other. Given a websocket connection, a server will send an event message to the browser through an open websocket connection, enabling direct interaction between events on a server and the browser.

ActionCable Architecture and Concepts
Traditionally, the websocket way of handling connections did not integrate well into a Rails application - the process of handling a request in Rails is fundamentally aligned with the request-response-end way HTTP connections are handled.

As such, ActionCable does not hook into the typical Rails MVC handling of requests, but adds another entry point to a Rails application - the ActionCable server: This server is, as of now, started as a different process dedicated to handling multiple open websocket connections while loading all components of the Rails application and providing developers the comfort of using its components. The Rails server will propagate messages to the ActionCable server by pushing them to a queue that the ActionCable server is listening to. In the current implementation, this role is given to a Redis instance.

We can explore the architecture and the way communication works between the components using the example of a chat application: It allows multiple users to connect to it and send messages to each other. Users receive messages from other users immediately i.e. users will not need to refresh the browser to see a new message but see it pop up at the end of the message list. You know, a chat like you would expect.

Let’s trace one possible way of propagation of a user’s message:
- A user opens the page in his browser which in turn opens a websocket connection to the ActionCable server (Websocket).
- A user sends the message by remote form submission to the Rails server (HTTP).
- The Rails server persists the message and publishes a message including the user and the message body to the queue. It sends an acknowledgement to the user. This ends the HTTP request.
- The ActionCable server receives the published message from the queue. It publishes the user and message body to all relevant open websocket connections.
- All relevant browsers connected to the ActionCable server receive the message and show it in the DOM.

Rails 5 ActionCable Architecture and Concepts

To allow for differentiation between different groups of users, ActionCable uses the concept of channels: A user may subscribe to a select few of all available channels and thus only receive messages meant for the given channels. If the chat application were to have multiple “rooms”, a user could subscribe to each room via a designated channel.

On the technical side, ActionCable uses EventMachine to handle connections and spawns a new Ruby thread for handling messages. This means that an ActionCable server will need to be multi-threaded.

So far so good, That’s it!!! See ya!!! :)

SaaS Cloud Computing

SaaS Cloud Computing

SaaS (Software as a Service) describes any cloud service where consumers are able to access software applications over the internet. The applications are hosted in “the cloud” and can be used for a wide range of tasks for both individuals and organisations. Google, Twitter, Facebook and Flickr are all examples of SaaS, with users able to access the services via any internet enabled device. Enterprise users are able to use applications for a range of needs, including accounting and invoicing, tracking sales, planning, performance monitoring and communications (including webmail and instant messaging).

SaaS is often referred to as software-on-demand and it is akin to renting software rather than buying it. With traditional software applications you would purchase the software upfront as a package and then install it onto your computer. The software’s licence may also limit the number of users and/or devices where the software can be deployed. Software as a Service users, however, subscribe to the software rather than purchase it, usually on a monthly basis. Applications are purchased and used online with files saved in the cloud rather than on individual computers.

There are a number of reasons why SaaS is beneficial to organisations and personal users alike:

No additional hardware costs
The processing power required to run the applications is supplied by the cloud provider.

No initial setup costs
Applications are ready to use once the user subscribes.

Pay for what you use
If a piece of software is only needed for a limited period then it is only paid for over that period and subscriptions can usually be halted at any time.

Usage is scalable
If a user decides they need more storage or additional services, for example, then they can access these on demand without needing to install new software or hardware.

Updates are automated
Whenever there is an update it is available online to existing customers, often free of charge. No new software will be required as it often is with other types of applications and the updates will usually be deployed automatically by the cloud provider.

Cross device compatibility
SaaS applications can be accessed via any internet enabled device, which makes it ideal for those who use a number of different devices, such as internet enabled phones and tablets, and those who don’t always use the same computer.

Accessible from any location
Rather than being restricted to installations on individual computers, an application can be accessed from anywhere with an internet enabled device.

Applications can be customised and whitelabelled
With some software, customisation is available meaning it can be altered to suit the needs and branding of a particular customer.

Office software is the best example of businesses utilising SaaS. Tasks related to accounting, invoicing, sales and planning can all be performed through Software as a Service. Businesses may wish to use one piece of software that performs all of these tasks or several that each perform different tasks. The required software can be subscribed to via the internet and then accessed online via any computer in the office using a username and password. If needs change they can easily switch to software that better meets their requirements. Everyone who needs access to a particular piece of software can be set up as a user, whether it is one or two people or every employee in a corporation that employs hundreds.

Why SaaS?
- There are no setup costs with SaaS, as there often are with other applications.
- SaaS is scalable with upgrades available on demand.
- Access to Software as a Service is compatible across all internet enabled devices.
- As long as there is an internet connection, applications are accessible from any location.

SaaS Market

SaaS Cloud Computing

So far so good, GeeKhmer SaaS Solutions is an expert in SaaS Development if you have many projects or questions pls contact us via bunlong.van@gmail.com. see ya!!! :)