Showing posts with label rant. Show all posts
Showing posts with label rant. Show all posts

Wednesday, April 23, 2014

Rails, how developers grow, and what's wrong with it

Rails is a great tool for automating the heck out of pumping out a quick and dirty site in short order.

It's a terrible tool for learning. The community isn't like the IRC communities I've been involved in. It's catering for a lot of developers early in their journey.

This is what I see on /r/rails or hacker news or being actively practised far too often:

  • I just discovered service objects
  • Test Driven Development/Unit Tests are dead, because it's too hard to stub out ActiveRecord.
  • Uh, guys, I started using after_save hooks and now my persistence layer is working with Sidekiq and the Mail server to kill all humans.
  • DHH said something outrageous! 60% of us are rushing to take his word as gospel right now; cause like, uh, nuts to the status quo!
  • Oh neat I discovered modules, and that classes are objects, so... like, I'm not going try to do evil... but... here's a gem I wrote that adds a search to all of the defined classes, because it's not like that could cause conflicts; right?

and it makes me cranky. It's actively harmful for people who don't have seasoned peers near them, ready to slide over a polite link on SOLID; or talk to you about the Principle of least astonishment.

Surely we can do better.

Where are the articles on

  • If you are testing your active record instances, that's not a unit test. Go extract your domain models and test those please.
  • Here's 10 terrible ways to write a component/gem/etc; because it's the people using your code that have to put up with your mistakes when your github repo becomes abandonware.
  • How to open up a github project to collaborators when you don't have the time to maintain your code anymore.
  • How to write a decent readme that sums up your gem/repo/etc.
  • What is Single Responsibility Principle? How does it apply within the context of ActiveRecord, Controllers and triggering after-create/after-save events?
  • Why are we OK to use FactoryGirl for tests (obviously useful), but much less willing to use the Factory Pattern to assemble our object graph; when convention breaks down in the face of complexity?
  • KISS: Why it's often worth writing code you might feel is imperfect if it helps your team collectively understand what the heck you are doing.
  • Why version control systems have a blame feature, and the startling reality the next guy looking after your system can track you down and beat you to death with your own severed legs.
  • Stop reading the ember.js documentation now, and just go look at the source code, it'll save us  all a load of time.
  • Magic is generally bad unless you are all wizards; but like Lord of the Rings it's freakin' easy for one or more of you to turn evil.

Interestingly, many these are framework and language agnostic - in my experience though, many framework centric types tend to only know their framework and community; so it distorts their understandings. 
It's not their fault - otherwise smart people have set out to solve a number of problems, then tried to generalise them having already learnt a significant amount of things the hard way - they just keep forgetting to share their learnings.


The best resources I had when I was learning was a mix of IRC (there is nothing like asking a stupid question and realising you just did so infront of the guy who invented the web, or the language you are using right now), and wikis like Portland Pattern Repository.



Tuesday, January 21, 2014

Incident Pit

One of the things that as struck me as a very accurate observation of complex systems is the Indicident Pit.

I see things like this through work, were small problems are left unchecked to create larger ones, and generally don't find myself stuck in the situation very often - it's made me quite disciplined about getting the small things right.

All of that hard earned wisdom didn't help today though - Christina doesn't know about Incident Pits.

The setup:

  • You'll need a Chihuahua.
  • You'll need a Cat.
  • You'll need a significantly larger dog who understandably wants to understand the cat, but can't; as it keeps running away.
  • You'll need a fence that lets the Chihuahua through it
  • You'll need to neglect upkeep of the Chihuahua's dog school lessons
  • You might want to add a greyhound, even if all it does it sit back and watch
  • And you'll need the Hottest City in the World.
Prior setup:
Take the Chihuahua from your relative's home, add it to your own home/zoo during the peak of the heatwave, because it's "too hot".

Step One:
Take the cat, and put it in a room to keep it away from the circus of dogs invading its otherwise comfortable house.

Step Two:
For no apparent reason, retrieve the cat and attempt to place it outside, via the dog occupied territories

Step Three:
When you realise the animosity between the larger dog and the cat you are now holding aloft in your hands has reached a renewed vigour, change your approach. Particularly when the large dog walks outside, the very place you are trying to put said cat, making the placement of any cats in any outdoors environment infeasible.

Step Four:
While all of this has been going on, your partner, working diligently from home (ie: me) sees your distress and finally decides to assist - let's face it, there is no neat way you are getting out of this.

Step Five:
Instead of waiting, holding the cat safe but scared out of harms way for your partner to help, immediately try to visit the other end of the house and "post" the cat via the cat door and commence walking away from the scenario.

Step Six:
Right now you've got one dog on the Porch, the gates are open and so is the screen door.
At about this time, observe as the Chihuahua causally strolls through the open screen door, across the porch, lawn and subsequently through the front fence - which, handily, takes a human longer to run around. Don't worry, your partner is on it.

Step Seven:
Chase the tiny dog. The large dog is loose too and getting a bit excited, but it still on the property.
Yelling takes place for a while. The gate gets closed. Large dog is now at least in one place. Fetch dog treats and an ineffective clicker, which seems to just accelerate the Chihuahua.

Step Eight:
Chihuahua runs, and thinks What a fantastic game! I run, they follow me! This is great! I bet I can make it all the way into this huge industrial area! 



View Larger Map

A car is fetched, but your partner is still trailing the dog, diligently and most importantly barefoot. Over hot pavement. Broken glass. Snake infested ex industrial dumping grounds.

Step Nine:
Every time your partner gets the Chihuahua's attention, think you can do better and give it a something else to pay attention to, even if you are further away.

Step Ten:
Finally catch the Chihuahua, so in one and a half hours the relative can pick it up.


What's the result?

Your partner now is

  • Suffering second degree burns, blistering
  • Not able to walk comfortably
And the Chihuahua is now so tired from all of the adventure, it's deciding to sit in his lap in what can only be described as 'calculated taunting' and a masquerade of innocence.

But wait! What about the greyhound?
Didn't move an inch the whole time.

Friday, February 01, 2013

Capistrano frustrates me


Here's why:

1) It defines its own class path. If you want to load a customised extension, good luck - it is seemingly unaware of gems you add via bundle; pointing at a git repository

2) Task definition and execution are not separated. require is replaced with load - and load means "load and run".

For example - https://github.com/capistrano/capistrano/blob/master/lib/capistrano/recipes/deploy/assets.rb
You cannot "just" load the code and do something like compile assets locally by drawing on commands generated by someone else.

3) The methods defined are too rigid. I would refactor every capistrano recipe out there into behaving in two ways. First, there's a lot of methods that generate commands to be run. Second, there's business logic which decides where those commands should be run.

Looking at the recipe above:

    task :clean, :roles => assets_role, :except => { :no_release => true } do
      run "cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:clean"
    end

You get no control there - the run() method will execute that remotely.
More frustrating: the commands are dumb strings. You don't really have the input escaping or knowledge of what's valid to put into the command in the code itself.

There needs to be flexibility, I agree, but some kind of basic input checking or modelling of what's happening should be more widely used.

4) The recipes tend to have no idea of where they are - you might as well write shell scripts to do most of it. Every time you see 'cd {absolute path}' is a good indication of this - smarter recipes would likely check current working directory (pwd); or keep a pointer to that.

The only thing that capistrano does better than pure bash scripts:

  • There's a lot pre-written, so you don't reinvent the wheel.
  • It has a dependency solver (before this do that, after that do this)


Saturday, September 08, 2012

rspec, let(), let!(), activerecord and global state

Yesterday at work I was beating my head against a brick wall. The setup we have is machinist, rspec, and devise in an otherwise standard rails application.

We have blueprints for Users and Profiles, which are all pretty straight forward. When doing coverage of security related scenarios, we've got a helper

def login_as
   user = User.make!

   # .. and bits to make a faux current_user
end

All fairly straight forward so far. A typical security test might look like this:

describe "cat" do
  login_user

  let(:profile) {
    Profile.make!(:user_id => @user.id)
  }


  let(:cat) {
    Cat.make!(:profile_id => profile.id)
  }
  
  let(:brush) {
    Brush.make!(:way => "wrong", :owner => @user.id)
  }

  it "should not allow users to rub them the wrong way" do
    should_not be_able_to :brush, cat
  end


  it "should allow users to feed them" do
    should be_able_to :feed, cat
  end
end

Seen the problem? Of course, let() is being used as a kind of setup(), and if the guts of our code actually relies on the relationships created by executing the above code, you'd expect a User, Profile, Cat and Brush to be made for each it() block.

You'd be wrong, like I was, so helpfully a colleague tells you to always use let!() to ensure it's executed and instantiated.


describe "cat" do
  login_user


  let!(:profile) {
    Profile.make!(:user_id => @user.id)
  }


  let!(:cat) {
    Cat.make!(:profile_id => profile.id)
  }
  
  let!(:brush) {
    Brush.make!(:way => "wrong", :owner => @user.id)
  }

  it "should not allow users to rub them the wrong way" do
    should_not be_able_to :brush, cat
  end


  it "should allow users to feed them" do
    should be_able_to :feed, cat
  end
end

... and the test still doesn't work as you'd expect, with using let!() as a setup().


describe "cat" do
  login_user


  let!(:profile) {
    Profile.make!(:user_id => @user.id)
  }


  let!(:cat) {
    Cat.make!(:profile_id => profile.id)
  }
  
  let!(:brush) {
    Brush.make!(:way => "wrong", :owner => @user.id)
  }

  it "should allow users to feed them" do
    should be_able_to :feed, cat
  end

  it "should not allow users to rub them the wrong way" do
    should_not be_able_to :brush, cat
  end

end


But this version does.
What?

So after much further staring, I remembered that let() and let!() are all about caching - you call it once and the result is persisted across tests.

That's not the case with login_user, which executes every time - so all of a sudden, @user.id is different in multiple places, and it's completely non obvious - let() is making global state.

Why would anyone want to add global state to testing frameworks? It just causes Spooky Action at a Distance, like above.
What do we want to do instead? There's a before() which should almost always be used, as it's a direct relation to setup() from the xUnit family of tools.
But let() is there, so what's it actually for?

Dan's rules for let()

  • Only use this for otherwise dynamic values or complex calculations that you do want to fix at a point in time.
    let(:time) { Time.now }
    let!(:number) { Math.random }
  • If you have to use let() as a setup type thing, try to make One Big Setup
  • Don't listen to co-workers unless they are offering to buy you a beer


Thursday, August 09, 2012

Rosewater Post Office

Why can't you open an hour earlier one day a week? No one who works in the city can possibly collect a package from you and get to work on time.
Alternatively, stay own an extra hour once a month or even open on the weekend.
When delivery companies ignore the instructions re placement of parcels and just deliver it to the nearest post office, that parcel is trapped until the owner has a half day off of work.
You need a half day because it takes a hour to get in to the city by bus, and after the peak transport windows slam shut, you have to wait about half an hour for that bus, if it deigns to arrive at all.

Starting at lunch never looks that good.

Others when faced with this problem have had packages delivered to work places - but this is not viable with bulky packages, or things where you need to wander through the city and onto a bus carrying heavy items.

So we have established the quick fix around opening hours, but what about the longer term?
Part of the problem is Web applications that don't provide delivery instruction capture.
In my view there should be a way to register a default delivery preference.
Australia Post has the trust and national reach to provide a service like this, and courier companies would undoubtedly like the idea of understanding what to do, rather than hauling the item off to the post office.

Here's how it works. The parcel delivery that fails is indicated by a card telling you where to go to get your package. On the back of it is a Qr code, and a plain text id plus link.
This token is secure, and let's you sign up. Your address its pre known.

You fill out the default instructions and agree to what ever you need to about sharing the info with third parties, and liability.

When you collect your package, the auspost agent certifies your id and the account is live.

You get annoyed at the first package, but not at the next.

Downsides - modern houses need a climate controlled secure delivery point, where authorised items can go in but other things cannot. Letter slot in the door 2.0.
This enables food delivery and other bulky items.
I am not an architect, but variations on dog doors, or a keyed deposit box seem to be a good starting point for what this could look like.

The second one is if you move house, your packages are left with the next occupant.
While this is an issue for mail, most people making purchases of a substantial nature are less likely to ask for it to be sent to the wrong place.

This seems doable. In fact, everyone who ever had their mail redirected already did the days capture part of this concept.

Monday, August 06, 2012

Things I miss in ruby

Type hints.
In php I could have type hints to recommend the interface I wanted to be injected into my method or ask for certain dependencies.

Sure, I can do it myself but that seems painful compared to what php gave me out of the box.
You don't have to be a jerk about it, but a little structure can go a long way.

Dependency injection.
For some reason many of the rails community read a post against DI frameworks, and decided it was ok not to do DI. Because if you duck type it a frame work is a philosophy.
The results speak for themselves. Clearly a level seven ruby developer wrote the ruby CAS rails plugin, which had no tests, is a super singleton, and does whatever the hell it wants with your controller. Define two alternative logout uris? Not on its watch!
I admit the design is from 2006 or earlier when not so many people were enlightened, but I am still stuck with it.

On a library designed by people who develop for computer literate universities.

Cut the cord people, stop doing magic and just make a simple utility class or three.

Defensive programming.
There seems to be a jerk who said not to program defensively.
Thanks, that guy. The result is execution of an app continues well pat the danger input point because of a general principle.
Think about it for a minute, with the observer pattern.
Some input happens, events trigger. Now there are fifteen event listeners and they all freak out half way through executing because off some failed expectation.
A simple exception to check the input often points directly at the problem, but in ruby land you just let execution continue in general.
Most dissuading about this, those that discuss it on the Web tend to agree that you should check input and bug out.
The point is not to fail silently. But rails land doesn't care. It is going to make poor code poorly while screaming look at my ruby FU.
If rails developers, by and large left the power offered by ruby well enough alone and coded for other humans, life would be far, far better.
KISS > DRY

Friday, July 06, 2012

My Kingdom for a Bug Fix

At my new work, the wireless keeps flaking out.

This wouldn't matter much if it wasn't for https://bugs.launchpad.net/ubuntu/+source/network-manager-applet/+bug/912702 - every day I'm closing ~50+ network authentication prompts.


Wednesday, June 13, 2012

Details

One of the things said to me as I left RP Data/ValEx was "Dan, you are into way too much detail".

I've been told that time and time again, but never had a good counter argument. It's hard to explain why making a link between two public websites which took about 12 months of championing to achieve is important; or fixing the login form to redirect you back to the original URI you were viewing are worthwhile.
I know from experience that small usability fixes like this can pay off in big ways - I've just had difficulty explaining what and why those matter.

I may finally have an answer: sweat the small stuff.


Sunday, January 09, 2011

XBMC vs Boxee vs file browsers

I ditched Boxee the other day. I was sick of it eating far too many resources, never quite keeping up with my mouse, fighting me with strange user interface metaphors; ignoring some files it would never recognize and providing me with not much enjoyment.

I installed XMBC and got rid of boxee. That was before I realized how good I had it: Boxee's scraper doesn't work off regexp and demand you do crazy things to your filesystem.

What I am struggling to understand is why I can't simply have a damned metadata layer and scraper for my desktop which plugs easily into the file browser.

All that both Boxee and XBMC are really doing is scrape, fetch, and organise. XBMC does it better with a few widgets like "recently discovered espisodes"; but even then; how hard is it?

I advocate the semantic web stack just because this is the very problem those tools should be able to solve in a trivial fashion; but I don't understand why this has not been achieved by anyone else in the last decade.

It's not like web scraping is mind bogglingly hard, nor matching little bits of string against certain sources. Musicbrainz solved the problem for mp3s some time ago: if the same can't be done for video content, I would be surprised. Additionally, it's not like relational databases are a new thing: they have been around for a bit, and that's all you really need: schema, inserts, and the web scraper organising it all.

So it really, really pains me when the Boxee/XBMC/Gnome world can't get it right. Why does Gnome/Gnautilus still treat media basically like this:

Whilst your original design might have been GUI filesystem explorer, what I actually want from you is GUI-like-XBMC-Metadata-displayer.

Wednesday, May 12, 2010

Medical Director 3 Feature Request: Google Health Sync

I want to walk into my Doctor's office, give him my Google Account, and let him pull down my google health profile.

Medical Director 3 meet Google Health Data API.

Once he's updated my record, he should be able to push a button and write new changes.

How long would it take a programmer like myself to build this if I were being lazy?
  • RESTful webservice client library
  • Controller to retrieve and map google client information to models (Patient, Doctor, Drug, Treatment, etc) - 2 weeks
  • User interface to copy new information from google account - 2 weeks
  • User interface to upload new information to google account - 1 week
  • Unit tests galore
  • Controller to create new information in a google account, duplicates be damned - 1 week
  • RELEASE IT AS OPEN SOURCE

Can I have it now please? I don't think I need $466.7 million to build it and roll it out; but I think it would achieve a hell of a lot of the same goals very quickly.

Don't like Google health because of privacy concerns? Add in another 3 months to connect up to Microsoft Health. Don't like either of those? Rely just on current systems.

Thanks God!


Well, that was quick.

Thursday, January 07, 2010

Internet explorer, Null!

What. Were. People. Thinking.

>new String(null).toString()
"null"


Ridiculous. Firefox doesn't do anything that stupid. Worse, it's broken my populate form from JSON code.


populate_data: function(report) {
for (key in report) {
if (!$(key)) {
continue;
}

$(key).value = report[key];
}
}

Wednesday, December 23, 2009

Dear Western Australia...

... I'm going to saw you off from the rest of us, starting at the Nullabor.

You rejected Daylight savings time, and ruined 3 years of me running around after you, chasing correct timezone calculations.

Enjoy being an island.
Signed,
Daniel O'Connor

Monday, November 16, 2009

Internet Explorer, String.concat() and Javascript 1.5

Hello, Internet Explorer... have you ever tried to join two strings together?

Not add them ambigiously with
"a" + "b"
but
String.concat("a", "b");
them?

I'm guessing none of your developers ever has, because it doesn't work.


Why this acceptable:
String.fromCharCode(12);

but for concat, you have to do:
String("a").concat("b");


?

Ridiculous.

Reblog this post [with Zemanta]

Saturday, June 20, 2009

Usability niggles, Ubuntu

Ubuntu's next release will have one hundred paper cuts as a focus - fixing usability issues for users.

Here's my best pet peeves.

  1. On windows, if I paste in a URL in an "open file" box, windows will fetch it for me, write it somewhere temporary, and give me that temporary path. Gnome is inconsistent - I can put a URL into a the 'Run' dialog, but gedit doesn't let me open it. However, if I start gedit from the command line, it will open it - gedit https://edge.launchpad.net/hundredpapercuts/
  2. 'Choose another application' when opening a file from firefox gets me to try and look around for the path of an application. As a newer user to linux, I often have to fall back to the terminal, and use things like 'which gedit' to get the right path. If the run dialog can work out which application I want, why can't firefox.
  3. With gedit, if I open a file which does not exist, it does not let me create said file - it just errors at me.


https://bugs.edge.launchpad.net/hundredpapercuts/+bug/18995





Reblog this post [with Zemanta]

Saturday, May 30, 2009

Things I hate about Facebook

1) It doesn't work well with Opera. Until I upgraded, I couldn't even type in the login box. It's still buggy.
2) The annoying sound the inbuilt chat client makes for new messages always interrupts my music.
3) It's bloated in firefox. I really notice it on my slower computer
4) It's so much of a walled garden. Why can't I import photos from flickr, or export them?
5) Photo upload in general sucks. The java applet is fail. The fix? Yahoo already made it for you.
6) My events: I finally worked out where the export-to-google-calendar link was, and they are published without a timezone. So now, I'm busy going to places at 3am.
7) You call it an API, but it's not an API to access data and remix it.
8) Where's my xmpp compatible chat, eh?

Basically, it irritates me that the developers they have working for them don't care about these things in the slightest.

Worse, it's sucking up and doing poorly all of the functionality I had in external services. My 2nd tier friends seem to use facebook chat over MSN or Google Talk.

On the other hand, LuckyCal almost promises to be useful.



















Reblog this post [with Zemanta]

Saturday, January 31, 2009

GEdit usability nitpicks

I like ubuntu/gnome's gedit for simple jobs when I'm not in the mood to wrestle vim.

Here's my pet peeves from a usability point of view

gedit 2.24.2 annoys me with:
1. Find and Replace across multiple documents should exist. Editplus does this with an 'all open documents' tickbox
2. If I open a filename which doesn't exist; don't show me an error. Create a new document, and when I save, create the file.
3. Give me a 'always reload when changed on disk' preference. I'm sick of clicking the buttons.



Reblog this post [with Zemanta]

Thursday, January 15, 2009

#nocleanfeed, strategy, and direction shifts

There's a growing buzz amongst the #nocleanfeed communities - the issue is being framed as a censorship and freedom of speech issue more and more.

This is being driven largely by the DLC.

I don't like that; it's not what motivates me; its not how I get people on side. We don't have freedom of speech here in Australia except around political issues. When has that ever stopped us from saying what we want, though? As a society, we're more than happy to say what we want. If someone says nasty things about us, we don't get our panties in a twist about it; we call them a wanker and ignore them.

What does motivate me is how dishonest the government has been through this process.

1) They call it a "clean feed"; but it won't actually work. A more descriptive name: thing that will annoy the shit out of you every 3 days on the internet.
2) It won't work like they want, but the government won't listen to their own trials, industry and the public. That makes them stupid.
3) Its a people issue. Not a technology issue. It is pretty obvious that is the case.
Think of it like gun control - guns don't kill people; people kill people. Answer: don't let stupid people have guns, or arrest stupid people who do stupid things with stupid guns.
4) Conroy is a wanker. He acts like a child and won't answer questions in Question Time. He makes a mockery of our political process
5) Conroy (his office at least) threatened Mark Newton's job. That's unaustralian, stupid, and the act of a first class fuckwit.
6) This childish fuckwit is spending our tax money on his 1984 reenactment wet dream. Why?
7) If it is a problem; how come I don't hear more about it happening to my friends, family and workmates - my child was preyed upon by an internet sicko? The truth is, it doesn't happen.
8) IF THIS IS SUCH A PROBLEM AFTER ALL; WHY NOT SPEND THE MONEY SOMEWHERE USEFUL, LIKE THE POLICE?

Reblog this post [with Zemanta]

Monday, January 12, 2009

OOXML, Wikipedia, and whitewashing

Image representing Microsoft as depicted in Cr...
Image via CrunchBase


I'm still pissed about OOXML, and the standardisation process.

Have a read of the wikipedia article.

That certainly doesn't sound like my understanding of it - there's about 3 lines of content throughout the entire thing talking about the criticism of it, and the standards process.

What the?
You would thinks that at least the technical arguments against OOXML would fit somewhere on the main page, wouldn't you.

So, delving deeper; we discover this is mainly due to one editor with a hard on for OOXML.

His about page doesn't tell you much, except:
"From that work I have various interest in interapplication communication, real time and batch textual data interfaces, electronic archiving, datawarehousing and flexible data in documents (of which we produce millions every year)."


If you look at his edits, it's all very pro-microsoft, ever since he joined the site.

Now that's not a bad thing by itself - after all, it might just be his area of expertise; but to have a minuscule amount of criticism over a file format which created street protests during the standardisation process; primarily due to his efforts - that's just not on.

Reblog this post [with Zemanta]

Thursday, November 27, 2008

Wrong Metrics

It was exam time recently. Lots of people talking about assignments and word limits.

"ARGH! I need to do a 30 trillion word essay on earwigs!" was not an uncommon utterance.

If there is anything about writing software that I know, lines of code is completely the wrong metric.

Understandability, correctness, and simplicity are far more useful from a software point of view.

You might say that the English language isn't designed for a computer - and I would argue that neither are modern programming languages.

I would suggest that a "word count" is just the same as lines of code. If we've spent 30 years or more knowing that lines of code is a horrible measurement, why hasn't this insight spread to other fields - particularly universities and centers of education?

Why are people still required to create essays of a given length?

Why not set a different requirement out: convincingly argue X in clear and rational terms?

Monday, November 24, 2008

Bus anger

I'm sick of Adelaide's transport system; or at least the people behind it.

Today, I missed my bus by 20 seconds. This was 8:37am on Prospect Road. I settled in for the wait, being a 'GO Zone', knowing that it should only be a maximum of 15 minutes before the next bus.

Finally, 9:00am rolls around, and so does the bus. 23 minutes have elapsed. 8 minutes late. I wouldn't mind, except the entire bus trip into work from my house takes only 21 minutes. The bus is so late, it could have gone to Adelaide in the elapsed time. What the hell was it doing?

Worse, I get on the bus, and they charge me $4.20 - full fare. If I had waited 60 more seconds, that would have been $2.60.

Frustrating, yes. If this were a one off, I'd live with it. Unfortunately, this is not the first time and I doubt it will be the last. This is a regular occurrence on my bus route.

So far, everyone I've met involved with public transport in SA has been a complete dolt. In my last encounter, I had been assured that Adelaide metro data would end up in Google Transit - but that was a year ago, and there has been a complete failure to do it.

Day to day travel costs have risen, the buses and trams remain uncomfortable, I get ticket inspectors acting like the gestapo trying to fine fare cheats, I get a substandard way to access transport information, and I have little faith in certain bus routes actually delivering buses when they say they will.

What gives? Am I alone in thinking this?