50 States of McMansion Hell: Campbell County, Wyoming

Howdy, folks! It’s time for us to go home, (big, ugly) home on the range. That’s right: It’s time for Wyoming. Now, when I think Wyoming, I think of rolling hills, ranches, incredible landscapes, and also that book about cowgirls that Dick Cheney’s wife wrote.

On that note:

This beautiful 5 bed/4.5 bath 6,000 square foot house was built in 2002. It can be all yours for around $700,000 USD.

Let’s mosey on down through this here estate.

Divorce Lawyer Foyer/Living Room

Now, I’ve been an architecture enthusiast for about a decade of my short, two and a half decade life and never have I seen something as both absurd and patently useless as whatever the hell that thing separating the foyer and the living room is. Does it have a structural purpose? No. Does it have an aesthetic purpose? Also no.

Kitchen

(Running for president voice) “Folks, when I become president, I assure you that the injustice - the absolute tyranny -  that is the island stovetop/wall oven combo will finally–” (crowd cheering) “– and I mean finally, be put deep into the ground where it belongs.”

Dining Room

The aged bronze chandeliers must have been on ultra clearance. Personally, as someone who loves a good deal, I cannot shame them for this.

Master Bedroom

What I can’t determine is what they were going for with the sponge paint on the walls. My closest approximation is tree bark, in which case what we see before us in fact is deeply offensive to the trees, who are innocent and don’t deserved to be involved in any of this.

Master Bathroom

I don’t care how nostalgic we as a culture get for the late nineties/early aughts, sponge paint should never come back. One, it’s sponge paint. Two, it is ridiculously labor intensive and frankly we could all be spending our last few years on this still-habitable earth doing something more worthwhile with our time, such as going outside or falling in love or destroying our brains on the internet.

You are seriously not ready for this

When making this picture I realized I desperately need to log off.

Bathroom 2

Computer, enhance.

Ok but you can’t even read that in the shower. If you’re that desperate for prophetic, non-digital bathroom reading material, buy a bottle of Dr. Bronners or, like, a copy of War and Peace.

Basement

Poor Little Julie. :(

Well, that wraps up our interior. Now, for the final frontier, on the frontier:

Rear Exterior

Somehow this house looks more like it’s made out of cardboard than the many, many houses on this blog that also look like they’re made out of cardboard.

Anyways, that does it for Wyoming! Stay tuned for next week’s Brutalism Post, Part II: What Brutalism is Not. Have a great weekend, folks.

If you like this post, and want to see more like it, consider supporting me on Patreon, ESPECIALLY if you also like donoteat01′s content on YouTube, because we will be doing a livestream collab on Patreon on August 25th, 2019 from 8-10PM EST!!!

There is a whole new slate of Patreon rewards, including Good House of the Week, Crowdcast streaming, and bonus essays!

Not into recurring donations or bonus content? Consider the tip jar! Or,Check out the McMansion Hell Store ! 100% of the proceeds from the McMansion Hell store go to charity!

Copyright Disclaimer: All photographs are used in this post under fair use for the purposes of education, satire, and parody, consistent with 17 USC §107. Manipulated photos are considered derivative work and are Copyright © 2019 McMansion Hell. Please email kate@mcmansionhell.com before using these images on another site. (am v chill about this)

Kate Wagner - McMansion Hell Live at Motorco!

Kate Wagner - McMansion Hell Live at Motorco!:

Howdy folks! If you’re in the NC area next Tuesday, come and see me talk about McMansions as well as do a live house roast at Motorco in Durham. The ticket sales go to fund NC Modernist Houses, a nonprofit that works to preserve the heritage of architectural modernism in North Carolina. 

When: Tuesday, July 23, 6pm

Where: Motorco, 723 Rigsbee Avenue, Durham

Tickets: Advance - $29
At the door - $39
VIP - $99
(you get to throw axes at pictures of McMansions with me afterward lol)

See y’all there! 

The Brutalism Post, Part One: Introduction

This is part one of a five-part post about Brutalism. 

University of Massachusetts at Dartmouth campus by Paul Rudolph. Photo via Library of Congress

No style of architecture so passionately divides even the most good-natured and level-headed people as Brutalism. The discourse surrounding Brutalism being “good” or “bad” is fierce and polemical. The “for” crowd lobbies on both aesthetic grounds – posting pictures of incredible and obscure structures and saying “I mean LOOK at this” – as well as political ones, citing in particular, how Brutalism was used to house thousands of people during the postwar period. 

On the other hand, the “against” crowd brings up the failed urbanism of Le Corbusier that gave us the freeways and slum clearance that split and displaced entire swaths of city fabric, proclaiming that only architects or architecture enthusiasts like Brutalism, and that this is a testament to how out of touch they are with everyday people. “If you had to live or work in these buildings,” they say, “you’d feel differently.” 

Unité d'Habitation by Le Corbusier. Photo by Thomas Nemeskeri, via Flickr (CC BY-NC-ND 2.0) 

I’ve been a spectator to this debate since I first lurked in the Skyscraper City forums as a high school freshman, ten years ago, when Brutalism itself sparked the interest in architecture that brings me here today. I have, as they say, heard both sides, and when asked to pick one, my response is unsatisfying. Though my personal aesthetic tastes fall on the side of “Brutalism is good,” I think the actual answer is  it’s deeply, deeply complicated. 

Still, what is it about Brutalism that makes it so divisive? Why does a short-lived substyle of modern architecture elicit such vehement passion in so many people? What does it even mean for a style of architecture to be “good” or “bad”? You can see why I’m drawn to finally sitting down and penning this series, which has been simmering at the back of my mind since I started McMansion Hell three years ago. (By the way, Happy Birthday to this blog!!!) 

Brutalism has a special way of inspiring us to ask big and difficult questions about architecture. “Is Brutalism good?” is really a question of “is any kind of architecture good?” - is architecture itself good? And what do we mean by good? Are we talking about mere aesthetic merits? Or is it more whether or not a given work of architecture satisfies the purpose for which it was built? Can architecture be morally good? Is there a right or wrong way to make, or interpret, a building? 

Ferrier Estate, a now-demolished social housing complex in South London. Photo by Tim Slessor via Flickr (CC BY-NC 2.0)  

I have bad news for you: the answers to all of these questions are complicated, nuanced, and unsatisfying. In today’s polemical and deeply divided world of woke and cancelled, nuance has gotten a bad rap, having been frequently misused by those acting in bad faith to create blurred lines in situations where answers to questions of morality are, in reality, crystal clear. This is not my intention here. 

For centuries, the philosophical discipline of aesthetics has tried in vain to articulate some kind of clearly defined standard by which we can delineate whether or not a work of art is good, bad, moral or amoral. Architecture makes this even more complicated because unlike literature, painting, music, or art, we have to live, work, and exist in architecture. Not only does the question of whether or not we can separate the art from the artist exist in architecture, so to do questions of whether or not we can separate the building from the politics, from the culture, from the time period, from the urbanism, from the socioeconomic system, from the entire contents of everyday life in which it exists. 

Orange County Government Center, Perspective Drawing, by Paul Rudolph. Photo via the Library of Congress.

Existential questions aside, there are other reasons to write about Brutalism. First, while we’ve been hemming and hawing about it online, we’ve lost priceless examples of the style to either demolition or cannibalistic renovation, including Paul Rudolph’s elegant Orange County Government Center, Bertrand Goldberg’s dynamic Prentice Women’s Hospital in Chicago, and the iconic Trinity Square, Gateshead complex, famous for the role it played in the movie Get Carter. My hope is that by bringing up the nuances of Brutalism before a broad and diverse audience, other buildings on the chopping block might be spared. 

On an even broader note, I think Brutalism is worth writing about simply because a lot of people are rightly confused as to what it even is.  The common practice of identifying Brutalism by the presence of a material - reinforced concrete - too broadly defines a style that belongs to a specific era and architectural praxis. There are so many buildings and styles called Brutalist that are not Brutalist that I’ve devoted the first two installments of this series to the subject “What Brutalism Is Not,” followed, of course, by “What is Brutalism?” The goal is that these two essays will be educational and interesting (with the added bonus of providing the reader with an arsenal of information that will make them as insufferable at dinner parties as I am.)

The third part in this series is devoted to the people of Brutalism - the architects, politicians, planners, writers, and philosophers, who signed their names to an architectural movement that spanned the globe. Finally, the last installment gathers all this information together and answers the question we’ve all been waiting for: is Brutalism good? 

The Kyoto International Conference Center, designed by Sachio Otani. Photo by Chris Guy, via Flickr. (CC BY-NC-ND 2.0) 

This is a series on Brutalism, but Brutalism itself demands a level of inquiry that goes beyond defining a style. Really, this is a series about architecture, and its relationship to the world in which it exists. Architects, as workers, artists, and ideologues, may dream up a building on paper and, with the help of laborers, erect it in the material world, but this is only the first part of the story. The rest is written by us, the people who interact with architecture as shelter; as monetary, cultural, and political capital; as labor; as an art; and, most broadly, as that which makes up the backdrop of our beautiful, complicated human lives. 

If you like this post, and want to see more like it, consider supporting me on Patreon!

There is a whole new slate of Patreon rewards, including Good House of the Week, Crowdcast streaming, and bonus essays!

Not into recurring donations or bonus content? Consider the tip jar! Or,Check out the McMansion Hell Store ! 100% of the proceeds from the McMansion Hell store go to charity!

Copyright Disclaimer: All photographs are used in this post under fair use for the purposes of education, satire, and parody, consistent with 17 USC §107. Manipulated photos are considered derivative work and are Copyright © 2019 McMansion Hell. Please email kate@mcmansionhell.com before using these images on another site. (am v chill about this)

50 States of McMansion Hell: Top 10 Waukesha County, Wisconsin McMansions

Howdy Folks! We’re continuing our out-of-order-for-dramatic-effect tour of the 50 States of McMansion Hell today with perhaps one of the most underrated McMansion counties in the country: Waukesha County, Wisconsin. These houses were so bizarre it was hard to choose just one to do a takedown of. So, without further ado… 

#10: Doom McGloom

This 2002 estate, thanks to the clever machinations of whoever took these photographs, looks less like an enticing investment property and more like a prime candidate for the Chernobyl ripoff set in America that has 2 stars and is only available on Amazon Prime. 

#9: Headquarters of Tree-Haters Anonymous

This 2004 manse is $1.4 million dollars and yet its creators couldn’t afford more than a single (invasive!) tree. I don’t know what kind of sociopath wakes up in the morning and actively hates everything taller than a malnourished shrub. Whoever they are, this is certainly the house for them. 

#8: Roofer’s Paradise 

A post-recession 2011 McMansion, this house clearly didn’t learn anything from the recent past. With many McMansions, I can conceive of ways to improve them to make them better. With this house, I simply do not know how to rectify its main problem: it’s, like, 90% roof. In my head I refer to houses like this as “turtle houses” but frankly this does a disservice to the noble turtle. 

#7: Haunted Geometry

This house was built in 2014, a time when people should definitely have known better. Its inclusion in this list is solely due to the absolutely bizarre geometry of its roof, a kind of geometry formerly unknown to mathematics until this time. Bonus points for the continued animosity to trees found in the wealthy populous of this county. 

#6: McEscher

Nothing about this house makes sense. I’m serious. I’ve looked at it from several different angles and have yet to perceive any coherent spatial logic to how it comes together. This is house is an SCP. It’s an X-Files case. House of Leaves was actually based on this house. It’s an Escherian nightmare. 0/10 would not go inside even if you paid me. 

#5: Obligatory Beigehaus

You know when a bad stand up comedian tells a joke that just keeps going way too long? The audience is like, okay, we get it, you need therapy, but he (and it’s always a he) just keeps going on and on. Well, this is the house equivalent of that. 

#4: House of Lumps

Whoever built this house was utterly incapable of picturing in their minds eye what a house should look like. The very conception of a house is foreign to them. They have never seen a children’s book with houses in it. They probably didn’t even have a childhood. 

#3: Play-doh Playhouse

This house made it so far in the countdown because it is, frankly, weird. I don’t know why it is painted the color of jaundice, or why they have transformed every gable into a hollow cavity longing for death. Lots of things are happening here, though none of them could appropriately be called “architecture.”

#2: Farmhouse Freak

Let your eyes glaze over as you look at this “farmhouse” - the more you look at it the less sense it makes. What are they farming, you ask? Why, turf grass of course! Bonus points for this image in which the house appears through a haze of ozone or something. 

And now, the moment you’ve been waiting for…

#1: Corinthian Catastrophe

It’s one thing to have oversized Corinthian columns on your absurd McManse, but it’s a whole new level of extra to spray paint the capitals gold. This house takes all the elements found in the other houses (treeless sociopathy, turret lust, garish mismatched windows, foam) and ramps it up to 11, which is why it earns the number one slot in the county. Also, as a bonus, I find it incredibly funny that they embossed the letter “C” everywhere. I guess whoever buys it either has to have a name starting with C or has their work cut out for them. The C represents the grade they got in home design class. 

Anyways, that’s it for Wisconsin, folks! Stay tuned for a special essay on whether or not brutalism is good, as well as the next installment of the 50 States: Wyoming. Have a great weekend. 

If you like this post, and want to see more like it, consider supporting me on Patreon!

There is a whole new slate of Patreon rewards, including Good House of the Week, Crowdcast streaming, and bonus essays!

Not into recurring donations or bonus content? Consider the tip jar! Or,Check out the McMansion Hell Store ! 100% of the proceeds from the McMansion Hell store go to charity!

Copyright Disclaimer: All photographs are used in this post under fair use for the purposes of education, satire, and parody, consistent with 17 USC §107. Manipulated photos are considered derivative work and are Copyright © 2019 McMansion Hell. Please email kate@mcmansionhell.com before using these images on another site. (am v chill about this)

McMansion Hell Cross Stitch Patterns

Howdy folks! My internet has been funny this week, thus, I had a lot of time to myself with no internet which invariably leads to…crafting. I made some McMansion Hell-themed cross stitch patterns in MacStitch and wanted to share them with all of you in case there are other folks out there who love to stitch. 

These pictures are just the previews - to download the full patterns, click here

Happy Stitching! See you soon with Wisconsin’s McMansion! 

Why Marie Kondo’s method is ideal for my ADHD

Why Marie Kondo’s method is ideal for my ADHD:

I got to write about how Marie Kondo helped with my ADHD! 

Frank Lloyd Wright Quarterly | Frank Lloyd Wright Foundation

Frank Lloyd Wright Quarterly | Frank Lloyd Wright Foundation:

Art by Ellen Surrey

Howdy! The lovely folks at the Frank Lloyd Wright Foundation let me take the reigns as guest-editor for the new edition of their quarterly, about FLW and pop culture! It was a great opportunity for me to revisit my love of Wright’s work, to invite some of my favorite architecture writers around to share their talents, and to draw arrows and text on top of a very famous and handsome building instead of the ugliest and dumbest houses imaginable. 

Topics include why FLW’s work and life remain so influential after all these years; a reverse McMansion Hell roast of Taliesin West, and a look at FLW in tv (including GoT!!), movies, and the entire genre of science fiction. (There’s also some really cute drawings of FLW sitting on the Iron Throne, cavorting with robots, etc.)

Stay tuned for the next installment of this blog, which will drop Thursday! 

I wasted a whole day making this chart for architects and so now you have to see it too

I wasted a whole day making this chart for architects and so now you have to see it too

interior design is my passion

interior design is my passion

Kate Wagneri made a website for myself (kate wagner)

i made a website for myself (kate wagner)

Top 5 Worst McMansions in SpaceHowdy folks! If you thought suburban sprawl couldn’t extend any...

Top 5 Worst McMansions in Space

Howdy folks! If you thought suburban sprawl couldn’t extend any further, encroaching and devouring everything in its path, you’re right! That’s why, in co-operation with a variety of shady Silicon Valley start-ups, property speculators and developers are now taking bold new steps in order to expand the iron grip of real estate by entering… the space market! The offerings have been slim, so far, but remain ever promising for readers of this website. Let’s take a look!

#5: 6341 Crater Drive, Moon

Hoping to cash in on a moon real estate boom, this lovely 6000 square foot “ranch” is located a mere 30 miles from the famous “Moon Landing” site. Tall ceilings abound, convenient for those who have to live in low gravity. Also includes granite countertops, and “bathrooms” to remind you of Earth’s luxuries. Only $500,000,000, HOA not included. 

#4: 443 Black Hole Lane, Jellyfish Nebula

Another sprawling rancher, this time drifting endlessly into the abyss somewhere near the Jellyfish Nebula. At 6349 square feet, there’s plenty of space for you to ponder the loneliness that is the universe and the ever-present fear that you may never see your family on Earth again. Before you change your mind, wait til you see that master bath, folks!

#3: 553 Discovery Place, Mars

This amazing 6 bedroom 11 bath Space Chateau is a great investment opportunity for those smart investors that want to cash in on the Mars craze and stay ahead of Elon Musk! Designed to blend into the harsh Mars terrain, this 7,530 sqft house is perfect for the land grabbing space colonizing enthusiast and faux royalty alike. 

#2: 11 Apollo Court, Moon

Did seeing Apollo 11 in IMAX get your moon juices flowing? Well do I have a house for you! If you like a good view, forget the hilly expanses of California, or the breezy seaside vibes of Florida - you can see it all from this elegant 7845 sqft Moon Estate featuring no-maintenance plastic landscaping, a 3-rover garage, and large, diverse windows allowing for nonstop views. This house is great for entertaining, but if someone knocks on your door DO NOT ANSWER IT and REPORT TO THE NEAREST AUTHORITIES. 

#1: 733 Starry Boulevard, Carina Nebula

This incredible 6 bedroom, 8 bathroom home features large, sweeping views of the ever-expanding universe in which all of us are unfortunately trapped like puny flies in a dish of olive oil. If you like elegant stonework, tall ceilings, and the tranquil silence afforded by the vacuum of space, than this Tudor European Estate complete with satellite is perfect for your discerning tastes. 

McMansion Hell would like to thank recent architecture school graduate Sean Maciel (@seanmaciel on Twitter) for creating locating via space real estate networks the images used in this post. 

If you like this post, and want to see more like it, consider supporting me on Patreon!

There is a whole new slate of Patreon rewards, including Good House of the Week, Crowdcast streaming, and bonus essays!

Not into recurring donations or bonus content? Consider the tip jar! Or,Check out the McMansion Hell Store ! 100% of the proceeds from the McMansion Hell store go to charity!

Copyright Disclaimer: All photographs are used in this post under fair use for the purposes of education, satire, and parody, consistent with 17 USC §107. Manipulated photos are considered derivative work and are Copyright © 2019 McMansion Hell. Please email kate@mcmansionhell.com before using these images on another site. (am v chill about this)

we did it folks

we did it folks

Ada Louise Huxtable (1921-2013)

Ada Louise Huxtable (1921-2013):

I wrote about the life and timelessness of the architecture critic Ada Louise Huxtable for The Architectural Review. 

The case against lawns

The case against lawns:

My latest for Curbed: The Case Against Lawns

In Honor of the College Admissions Scandal

As a fine purveyor of content devoted to how wild and bad the very wealthy can be, even I was shocked at the news of a huge racketeering scandal in which lots of rich parents do illegal crimes in order to get their kids into certain universities, instead of you know, doing legal crimes like paying for buildings or having lunch with the dean Carmela Soprano style. 

The scam was apparently orchestrated by a Mr. William Singer, whose organization The Key Worldwide Foundation was used to disguise parental bribes to athletics coaches and other school personnel under the guise of a nonprofit. Anonymous sources tell McMansion Hell that Mr. Singer has unfortunately had to sell his Newport Beach, CA beach house in the wake of the scandal. Fortunately, McMansion Hell has received a photograph of said house from multiple sources: 

image

Indeed, as many are aware, multiple celebrity couples were wrapped up in the scene as well. Felicity Huffman and William Macy, are one such couple. And while an exterior photo of Huffman and Macy’s home remains elusive, here is perhaps the best of the interior shots obtained by McMansion Hell. 

Please note the decorative color-coded books and the sad ukelele casually draped across a stack of dictionaries, both of which are totally used by the residents. 

image

Finally, we have the “Spanish Colonial” house of Lori Loughlin and Massimo (of Target fame) Giannulli, who paid hundreds of thousands of dollars to secure their Instagram influencer daughter a spot at USC.  

image

McMansion Hell would like to thank sources both anonymous and public from Twitter for providing these images, and for the grossly unequal shitshow that is the American college admissions process and its gaming by the rich and powerful for contributing to this content, which, the reader is reminded, is covered by fair use. 

In movies when our planet is in great danger, all world leaders gather together and do whatever they can to save it. In real life, they don't don't do anything.

By /u/_ibisette_

submitted by /u/_ibisette_ to r/Showerthoughts
[link] [comments]

Would you support a mandatory environmental service for adults 18 - 30 ? Kind of like military service but instead of fighting you must spend a few months on reforestation projects or ocean cleanup ? Why or why not ?

By /u/fdgjuki

submitted by /u/fdgjuki to r/AskReddit
[link] [comments]

How i imagine All of October

By /u/Exotic_af

How i imagine All of October submitted by /u/Exotic_af to r/funny
[link] [comments]

The Catholic Church accused us of faking the plaque, so we decided to provide them with some evidence they can't deny

By /u/the-Chaser

The Catholic Church accused us of faking the plaque, so we decided to provide them with some evidence they can't deny submitted by /u/the-Chaser to r/australia
[link] [comments]

I just woke up now with a loud noise outside and there is A FUCKING PLANE IN MY STREET WTF IS GOING ON???

By /u/maxxtesla

I just woke up now with a loud noise outside and there is A FUCKING PLANE IN MY STREET WTF IS GOING ON??? submitted by /u/maxxtesla to r/teenagers
[link] [comments]

Blessed_hoodie

By /u/fort_one-e

Blessed_hoodie submitted by /u/fort_one-e to r/blessedimages
[link] [comments]

MRW I'm sitting on my front porch when a SWAT truck pulls up out front, and proceeds to raid my neighbors house

By /u/metaknight95

MRW I'm sitting on my front porch when a SWAT truck pulls up out front, and proceeds to raid my neighbors house submitted by /u/metaknight95 to r/reactiongifs
[link] [comments]

An insult with a great ending

By /u/shantanu011

An insult with a great ending submitted by /u/shantanu011 to r/rareinsults
[link] [comments]

Tinder over 3 years (18-21 Male) [OC]

By /u/Tyreathian

submitted by /u/Tyreathian to r/dataisbeautiful
[link] [comments]

Streamers quitting a $400,000 content creator tournament

By /u/Enixooo

Streamers quitting a $400,000 content creator tournament submitted by /u/Enixooo to r/FortNiteBR
[link] [comments]

Me in 3 years

By /u/RehmanAbraham

Me in 3 years submitted by /u/RehmanAbraham to r/teenagers
[link] [comments]

What does $1000 get you for your hobby?

By /u/huckabizzl

submitted by /u/huckabizzl to r/AskReddit
[link] [comments]

AITA for making my MIL pay for food I ordered after she ruined the one we had?

By /u/whateverpastas

My wife and I were hosting a barbecue on sunday. Half an hour before the guests arrived we were busy preparing food and my MIL was helping us.

While I was busy doing something else I asked my MIL to take the pasta salad I had prepared out of the fridge and taste for seasoning. She said that it needed more salt and I told her to add it, but not use the big salt shaker we have, because too much comes out of it. She used it anyways and oversalted the dish. I was really angry and tried to fix it with more mayo but it just didn't taste good. I told my MIL that I would have to order a new pasta salad from a local place, which she said wasn't necessary but I did anyways. About 15 minutes after the guests arrived I see the delivery guy at our front door. I tell my MIL to get up and she asks why. I tell her that the delivery guy is here so she can pay him. She looks at me stunned and asks "why should I pay him". So I have to expain that because she ruined the pasta salad she has to pay for the new one. She finally gets up to pay him but it's incredibly awkward and my wife is giving me the death stare. The guests seem kinda awkward too and my MIL didn't talk to me at all for the rest of the party.

After the guests were gone my wife flipped her shit at me. She called me an imbecile and an asshole and said that I embarassed myself and damaged the realationship to her mother. It's been three days and she's still angry about this. AITA?

Edit: Ok, I think the judgement has been clear. I will try to fix it, although I'm not sure yet how to.

I also wanted to say that it wasn't my intention to emberass MIL, I hoped that she would get up so I could explain it to her in private.

submitted by /u/whateverpastas to r/AmItheAsshole
[link] [comments]

TIL that if a pregnant woman has organ damage, the baby in her womb sends stem cells to help repair the organ.

By /u/memehehe

TIL that if a pregnant woman has organ damage, the baby in her womb sends stem cells to help repair the organ. submitted by /u/memehehe to r/todayilearned
[link] [comments]

My wife’s ER bill came in today..

By /u/dongutierrez96

My wife’s ER bill came in today.. submitted by /u/dongutierrez96 to r/Wellthatsucks
[link] [comments]

Keeping Busy

By Ellis Morning

Djungarian Hamster Pearl White run wheel

In 1979, Argle was 18, happy to be working at a large firm specializing in aerospace equipment. There was plenty of opportunity to work with interesting technology and learn from dozens of more senior programs—well, usually. But then came the day when Argle's boss summoned him to his cube for something rather different.

"This is a listing of the code we had prior to the last review," the boss said, pointing to a stack of printed Fortran code that was at least 6 inches thick. "This is what we have now." He gestured to a second printout that was slightly thicker. "I need you to read through this code and, in the old code, mark lines with 'WAS' where there was a change and 'IS' in the new listing to indicate what it was changed to."

Argle frowned at the daunting paper mountains. "I'm sorry, but, why do you need this exactly?"

"It's for FAA compliance," the boss said, waving his hand toward his cubicle's threshold. "Thanks!"

Weighed down with piles of code, Argle returned to his cube with a similarly sinking heart. At this place and time, he'd never even heard of UNIX, and his coworkers weren't likely to know anything about it, either. Their development computer had a TMS9900 CPU, the same one in the TI-99 home computer, and it ran its own proprietary OS from Texas Instruments. There was no diff command or anything like it. The closest analog was a file comparison program, but it only reported whether two files were identical or not.

Back at his cube, Argle stared at the printouts for a while, dreading the weeks of manual, mind-numbing dullness that loomed ahead of him. There was no way he'd avoid errors, no matter how careful he was. There was no way he'd complete this to every stakeholder's satisfaction. He was staring imminent failure in the face.

Was there a better way? If there weren't already a program for this kind of thing, could he write his own?

Argle had never heard of the Hunt–McIlroy algorithm, but he thought he might be able to do line comparisons between files, then hunt ahead in one file or the other until he re-synched again. He asked one of the senior programmers for the files' source code. Within one afternoon of tinkering, he'd written his very own diff program.

The next morning, Argle handed his boss 2 newly printed stacks of code, with "WAS -->" and "IS -->" printed neatly on all the relevant lines. As the boss began flipping through the pages, Argle smiled proudly, anticipating the pleasant surprise and glowing praise to come.

Quite to Argle's surprise, his boss fixed him with a red-faced, accusing glare. "Who said you could write a program?!"

Argle was speechless at first. "I was hired to program!" he finally blurted. "Besides, that's totally error-free! I know I couldn't have gotten everything correct by hand!"

The boss sighed. "I suppose not."

It wasn't until Argle was much older that his boss' reaction made any sense to him. The boss' goal hadn't been "compliance." He simply hadn't had anything constructive for Argle to do, and had thought he'd come up with a brilliant way to keep the new young hire busy and out of his hair for a few weeks.

Writer's note: Through the ages and across time, absolutely nothing has changed. In 2001, I worked at a (paid, thankfully) corporate internship where I was asked to manually browse through a huge network share and write down what every folder contained, all the way through thousands of files and sub-folders. Fortunately, I had heard of the dir command in DOS. Within 30 minutes, I proudly handed my boss the printout of the output—to his bemusement and dismay. —Ellis

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

Coded Smorgasbord: Unstrung Manager

By Remy Porter

Deon was on a contract-to-hire job. In the beginning, it sounded like a good job. Then he looked at the C# codebase. It didn’t take him long to decide that this wasn’t going to be a job he’d work at full time. Still, he’d be happy to continue contracting, because it was quite obvious that it would be a lot of billable hours.

How many is “a lot”? Well, let’s talk about their StringManager class. A common WTF is a “god” class that does a thousand different things. Here’s a class made up of nothing but static functions which is 1800 lines long. This wasn’t assembled by a junior developer or an intern, but their senior technical lead, who was adamant that this was the right way to program, and god damn anybody who said otherwise.

		public static DateTime ConvertCharacterDateToDateTime(string inputMM,
			string inputDD,
			string inputYYYY)
		{
			inputMM = initrode.utilities.StringManager.Fill(inputMM,
				"0",    /* Zero-fill */
				true,   /* From Left */
				2);
			inputDD = initrode.utilities.StringManager.Fill(inputDD,
				"0",    /* Zero-fill */
				true,   /* From Left */
				2);

			int _MM = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputMM);
			int _DD = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputDD);
			int _YYYY = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputYYYY);
			return new DateTime(_YYYY,
				_DD,
				_MM);
		}

This is just a little taste, a small aperitif. Convert three strings into a datetime object. For some reason, this requires padding to be added to the strings. For some reason, all the calls to StringManager methods are fully prefixed with their namespace, which is utterly unnecessary from inside that namespace.

But, I mean, dates are “easy mode”. How about FormatDecimalAsString?

        public static string FormatDecimalAsString
                            (
                                decimal inputDecimal
                                , int inputNumberOfDecimalPlaces
                            )
        {
            StringBuilder _decimalStringBldr =
                new StringBuilder();
            string _tempDecimalString = inputDecimal.ToString();
            //No decimal point
            if (_tempDecimalString.IndexOf(".") < 0)
            {
                _decimalStringBldr.AppendFormat
                (
                    "{0}.{1}"
                    , _tempDecimalString
                    , initrode.utilities.StringManager.Fill
                        (
                            ""    //string input
                            , "0"  //string fillChars
                            , true //bool boolFromLet
                            , inputNumberOfDecimalPlaces    // int maxLength
                        )
                );
            }
            //"." exists
            else
            {
                //If "." at start of string.
                if (_tempDecimalString.IndexOf(".") == 0)
                {
                    _decimalStringBldr.AppendFormat
                    (
                        "0.{0}"
                        ,   initrode.utilities.StringManager.Fill
                            (
                                _tempDecimalString.Substring(1)    //string input
                                , "0"  //string fillChars
                                , false //bool boolFromLeft
                                , inputNumberOfDecimalPlaces    // int maxLength
                            )
                    );
                }
                // "." not at start.
                else
                {
                    //  "." at very end.
                    if (_tempDecimalString.IndexOf(".") == (_tempDecimalString.Length - 1))
                    {
                        _decimalStringBldr.AppendFormat
                        (
                            "{0}.{1}"
                            , _tempDecimalString.Substring(0, (_tempDecimalString.Length - 1))
                            , initrode.utilities.StringManager.Fill
                                (
                                    _tempDecimalString.Substring(1)    //string input
                                    , "0"  //string fillChars
                                    , false //bool boolFromLeft
                                    , inputNumberOfDecimalPlaces    // int maxLength
                                )
                        );
                    }
                    // "." in middle.
                    else
                    {
                        ArrayList _parts =
                            initrode.utilities.StringManager.splitIntoArrayList
                            (
                               _tempDecimalString
                               ,"."
                            );
                        if (_parts.Count == 2)
                        {
                            _decimalStringBldr.AppendFormat
                            (
                                "{0}.{1}"
                                , initrode.utilities.StringManager.StripWhitespace
                                    (
                                        _parts[0].ToString()
                                    )
                                , initrode.utilities.StringManager.Fill
                                    (
                                        initrode.utilities.StringManager.StripWhitespace
                                        (
                                            _parts[1].ToString()
                                        )
                                        ,"0"  //string fillChars
                                        ,false //bool boolFromLeft
                                        , inputNumberOfDecimalPlaces //int maxLength
                                    )
                            );
                        }
                    }
                }
            }
            return _decimalStringBldr.ToString();
        }

Well, I’m noticing a trend, anyway, in that the developer really likes to break things across lines, so our “1800” line initial estimate is probably overblown. Of course, with the number of indented levels in here, combined with using fully qualified names on every line, you sort of have to.

I’m noticing a lot of calls to StripWhitespace. I wonder how that’s implemented…

		public static string StripWhitespace(string input)
		{
			string whitespaceCharacters = " \n\t\r";
			string tempString = input;
			StringBuilder bldr = new StringBuilder();
			for(int i=0; i < tempString.Length; i++)
			{
                if (i >= tempString.Length)
                    return bldr.ToString();
				string loopChar = tempString.Substring(i,1);
				if (whitespaceCharacters.IndexOf(loopChar) < 0)
				{
					bldr.Append(loopChar);
				}
			}
			return bldr.ToString();
		}

Sure, just iterate across every character. But hey, while I was CTRL+Fing for this, I saw StripWhitespaceFromEnds. I wonder what that does.

        public static string StripWhitespaceFromEnds(string input)  
        {
            string whitespaceCharacters = " \n\t\r";  
            string tempString = input;  
            if (initrode.utilities.StringManager.IsEmptyString(tempString) == true) return "";  
            tempString = input;
            
            for(int i=0; i < tempString.Length; i++)  
            {
                string loopChar = tempString.Substring(i,1);  
                if (whitespaceCharacters.IndexOf(loopChar) < 0)  
                {
                    tempString = tempString.Substring(i);  
                    break;  
                }
            }  
            for(int i=(tempString.Length - 1); i >= 0; i--)  
            {
                string loopChar = tempString.Substring(i,1);  
                if (whitespaceCharacters.IndexOf(loopChar) < 0)  
                {
                    tempString = tempString.Substring(0,i+1);  
                    break;  
                }
            }  
            return tempString;  
        }

Oh yeah, two for loops from either side. What else would I expect. Hey, what does IsEmptyString do? Is it just a wrapper around the built-in, IsNullOrEmpty? Of course not!

    public static bool IsEmptyString(string input)
  {
    if (input == null) return true;
    input = StripWhitespace(input);
    if (input.Length == 0) return true;
    return false;
  }

Oh, of course, we have to check if it contains whitespace before we remove the whitespace it contains. What was I thinking?

Y’know, at the very top of the class, I noticed:

  public const string CHARACTERS_IN_EMAIL_ADDRESS_PART = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";

They must use that when validating an email address.

  public static bool IsValidEMailAddress(string inputEMailAddress)
  {
    if (initrode.utilities.StringManager.IsEmptyString(inputEMailAddress) == true) return false;
    if (inputEMailAddress.IndexOf("@") < 0) return false;
    
    ArrayList _parts = initrode.utilities.StringManager.splitIntoArrayList(inputEMailAddress,"@");
    for (int _partCtr = 0; _partCtr < _parts.Count; _partCtr++)
    {
      string _part = _parts[_partCtr].ToString();
      if (initrode.utilities.StringManager.IsEmptyString(_part) == true) return false;
      if (_part.IndexOf(@".") >= 0)
      {
        ArrayList _periodDelimitedParts = initrode.utilities.StringManager.splitIntoArrayList(_part,@".");
        for (int _periodDelimitedPartCtr = 0; _periodDelimitedPartCtr < _periodDelimitedParts.Count; _periodDelimitedPartCtr++)
        {
          string _periodDelimitedPart = _periodDelimitedParts[_periodDelimitedPartCtr].ToString();
          if (initrode.utilities.StringManager.IsEmptyString(_periodDelimitedPart) == true) return false;
          if (initrode.utilities.StringManager.IsValidEMailAddressPart(_periodDelimitedPart) == false) return false;
        }
      }
      else
      {
        if (initrode.utilities.StringManager.IsValidEMailAddressPart(_part) == false) return false;
      }
    }
    return true;
  }

Oh, not here. What about in that IsValidEMailAddressPart?

  public static bool IsValidEMailAddressPart(string inputPart)
  {
    if (initrode.utilities.StringManager.IsEmptyString(inputPart) == true) return false;
    inputPart = initrode.utilities.StringManager.StripWhitespace(inputPart);
    for (int i = 0; i < inputPart.Length; i++)
    {
      string _character = inputPart.Substring(i,1);
      if (initrode.utilities.StringManager.CHARACTERS_IN_EMAIL_ADDRESS_PART.IndexOf(_character) < 0) return false;
    }
    return true;
  }

Ah, there it is. Hey, notice how it calls IsEmptyString and StripWhitespace, but also remember how IsEmptyString also calls StripWhitespace? Here’s a fun exercise for the readers: how many times do we access every character in the string?

I think nearly every method in this class could be an article in itself. And this is just a slice out of a large codebase where everything is done this way. There are NumberManager classes. DatabaseManager classes. Static methods everywhere.

If you need more proof, here is the entirety of the StringManager.cs class, all 1,800 lines of it:

using System;
using System.Collections;
using System.Text.RegularExpressions;
using System.Text;

namespace initrode.utilities
{
	/** Class:  StringManager
	 *	Contains static string handling routines. 
	 */
	public class StringManager
	{
		public const string CHARACTERS_IN_EMAIL_ADDRESS_PART = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";


		public static double ConvertCharacterPartsToDecimal(string inputWhole,
			string inputDecimal)
		{
			inputWhole = initrode.utilities.StringManager.Fill(inputWhole,
				"0",    /* Zero-fill */
				true,   /* From Left */
				8);
			inputDecimal = initrode.utilities.StringManager.Fill(inputDecimal,
				"0",    /* Zero-fill */
				false,   /* From Right */
				2);

			int _whole = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputWhole);
			double _decimal = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputDecimal);
			_decimal = _decimal/100;
			double _returnDecimal = (double) _whole + _decimal;
			return _returnDecimal;
		}
        public static void  ParseCommaDelimitedNameToLastNameAndFirstWordOfFirstName
                            (
                                string inputCommaDelimitedName
                                , bool inputAllCaps
                                , out string outputLastName
                                , out string outputFirstWordOfFirstName
                            )
        {
            inputCommaDelimitedName = inputCommaDelimitedName.Replace("'", string.Empty);

            outputLastName = "";
            outputFirstWordOfFirstName = "";

            if (inputCommaDelimitedName.IndexOf(",") < 1)
                return;
            ArrayList _nameParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    inputCommaDelimitedName
                    ,","
                );
            if (_nameParts.Count == 1) return;

            outputLastName =
                initrode.utilities.StringManager.StripWhitespaceFromEnds
                (
                    _nameParts[0].ToString()
                );
            string _firstAndMiddleName =
                initrode.utilities.StringManager.StripWhitespaceFromEnds
                (
                    _nameParts[1].ToString()
                );
            if (_firstAndMiddleName.IndexOf(" ") >= 0)
            {
                ArrayList _firstAndMiddleNameParts =
                    initrode.utilities.StringManager.splitIntoArrayList
                    (
                        _firstAndMiddleName
                        , " "
                    );
                outputFirstWordOfFirstName =
                    initrode.utilities.StringManager.StripWhitespaceFromEnds
                    (
                        _firstAndMiddleNameParts[0].ToString()
                    );
            }
            else
            {
                outputFirstWordOfFirstName =
                    initrode.utilities.StringManager.StripWhitespaceFromEnds
                    (
                        _firstAndMiddleName
                    );
            }
            if (inputAllCaps == true)
            {
                outputFirstWordOfFirstName = outputFirstWordOfFirstName.ToUpper();
                outputLastName = outputLastName.ToUpper();
            }
        }
		public static DateTime ConvertCharacterDateToDateTime(string inputMM,
			string inputDD,
			string inputYYYY)
		{
			inputMM = initrode.utilities.StringManager.Fill(inputMM,
				"0",    /* Zero-fill */
				true,   /* From Left */
				2);
			inputDD = initrode.utilities.StringManager.Fill(inputDD,
				"0",    /* Zero-fill */
				true,   /* From Left */
				2);

			int _MM = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputMM);
			int _DD = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputDD);
			int _YYYY = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputYYYY);
			return new DateTime(_YYYY,
				_DD,
				_MM);
		}
        public static string FormatDecimalAsString
                            (
                                decimal inputDecimal
                                , int inputNumberOfDecimalPlaces
                            )
        {
            StringBuilder _decimalStringBldr =
                new StringBuilder();
            string _tempDecimalString = inputDecimal.ToString();
            //No decimal point
            if (_tempDecimalString.IndexOf(".") < 0)
            {
                _decimalStringBldr.AppendFormat
                (
                    "{0}.{1}"
                    , _tempDecimalString
                    , initrode.utilities.StringManager.Fill
                        (
                            ""    //string input
                            , "0"  //string fillChars
                            , true //bool boolFromLet
                            , inputNumberOfDecimalPlaces    // int maxLength
                        )
                );
            }
            //"." exists
            else
            {
                //If "." at start of string.
                if (_tempDecimalString.IndexOf(".") == 0)
                {
                    _decimalStringBldr.AppendFormat
                    (
                        "0.{0}"
                        ,   initrode.utilities.StringManager.Fill
                            (
                                _tempDecimalString.Substring(1)    //string input
                                , "0"  //string fillChars
                                , false //bool boolFromLeft
                                , inputNumberOfDecimalPlaces    // int maxLength
                            )
                    );
                }
                // "." not at start.
                else
                {
                    //  "." at very end.
                    if (_tempDecimalString.IndexOf(".") == (_tempDecimalString.Length - 1))
                    {
                        _decimalStringBldr.AppendFormat
                        (
                            "{0}.{1}"
                            , _tempDecimalString.Substring(0, (_tempDecimalString.Length - 1))
                            , initrode.utilities.StringManager.Fill
                                (
                                    _tempDecimalString.Substring(1)    //string input
                                    , "0"  //string fillChars
                                    , false //bool boolFromLeft
                                    , inputNumberOfDecimalPlaces    // int maxLength
                                )
                        );
                    }
                    // "." in middle.
                    else
                    {
                        ArrayList _parts =
                            initrode.utilities.StringManager.splitIntoArrayList
                            (
                               _tempDecimalString
                               ,"."
                            );
                        if (_parts.Count == 2)
                        {
                            _decimalStringBldr.AppendFormat
                            (
                                "{0}.{1}"
                                , initrode.utilities.StringManager.StripWhitespace
                                    (
                                        _parts[0].ToString()
                                    )
                                , initrode.utilities.StringManager.Fill
                                    (
                                        initrode.utilities.StringManager.StripWhitespace
                                        (
                                            _parts[1].ToString()
                                        )
                                        ,"0"  //string fillChars
                                        ,false //bool boolFromLeft
                                        , inputNumberOfDecimalPlaces //int maxLength
                                    )
                            );
                        }
                    }
                }
            }
            return _decimalStringBldr.ToString();
        }
		public static DateTime ConvertTimestampStringToDateTime(string inputTimestampInStringFormat)
		{
			ArrayList _parts = initrode.utilities.StringManager.splitIntoArrayList(inputTimestampInStringFormat," ");
			if (_parts.Count != 2) return new DateTime(1900,1,1);
			string _date = initrode.utilities.StringManager.StripWhitespace(_parts[0].ToString());
			string _timeAndMilliseconds = initrode.utilities.StringManager.StripWhitespace(_parts[1].ToString());

            bool _boolValidDateInMM_DD_YYYY = false;
            bool _boolValidDateInYYYY_MM_DD = false;
            _boolValidDateInMM_DD_YYYY = 
                initrode.utilities.StringManager.IsValidDateInMM_DD_YYYYFormat(_date);
            _boolValidDateInYYYY_MM_DD =
                initrode.utilities.StringManager.IsValidDateInYYYYDashMMDashDDFormat(_date);

			if (
                    _boolValidDateInMM_DD_YYYY == false
                    &&
                    _boolValidDateInYYYY_MM_DD == false
                )
				return new DateTime(1900,1,1);
			string _dateYYYY = "";
			string _dateMM = "";
			string _dateDD = "";
			
			ArrayList _dateParts;
            if (_boolValidDateInMM_DD_YYYY == true)
            {
                _dateParts
                    = initrode.utilities.StringManager.splitIntoArrayList
                        (
                            _date
                            , "/"
                        );
                if (_dateParts.Count != 3) return new DateTime(1900, 1, 1);
                _dateYYYY = initrode.utilities.StringManager.StripWhitespace(_dateParts[2].ToString());
                _dateMM = initrode.utilities.StringManager.StripWhitespace(_dateParts[0].ToString());
                _dateDD = initrode.utilities.StringManager.StripWhitespace(_dateParts[1].ToString());

            }
            if (_boolValidDateInYYYY_MM_DD == true)
            {
                _dateParts
                    = initrode.utilities.StringManager.splitIntoArrayList
                        (
                            _date
                            , "-"
                        );
                if (_dateParts.Count != 3) return new DateTime(1900, 1, 1);
                _dateYYYY = initrode.utilities.StringManager.StripWhitespace(_dateParts[0].ToString());
                _dateMM = initrode.utilities.StringManager.StripWhitespace(_dateParts[1].ToString());
                _dateDD = initrode.utilities.StringManager.StripWhitespace(_dateParts[2].ToString());
            }
			ArrayList _timeAndMillisecondParts =	initrode.utilities.StringManager.splitIntoArrayList
													(
														_timeAndMilliseconds
														,"."
													);
			if (_timeAndMillisecondParts.Count != 2) return new DateTime(1900,1,1);
			string _timeMilliseconds = initrode.utilities.StringManager.StripWhitespace(_timeAndMillisecondParts[1].ToString());
			if (initrode.utilities.StringManager.IsValidNumber(_timeMilliseconds) == false) return new DateTime(1900,1,1);
			if (Convert.ToInt32(_timeMilliseconds) > 999) return new DateTime(1900,1,1);
			
			string _time = initrode.utilities.StringManager.StripWhitespace(_timeAndMillisecondParts[0].ToString());
			ArrayList _timeParts =	initrode.utilities.StringManager.splitIntoArrayList
													(
														_time
														,":"
													);
			if (_timeParts.Count != 3) return new DateTime(1900,1,1);
			string _timeHours = initrode.utilities.StringManager.StripWhitespace(_timeParts[0].ToString());
			string _timeMinutes = initrode.utilities.StringManager.StripWhitespace(_timeParts[1].ToString());
			string _timeSeconds = initrode.utilities.StringManager.StripWhitespace(_timeParts[2].ToString());
			StringBuilder _timeBldr = new StringBuilder();
			_timeBldr.Append(	initrode.utilities.StringManager.Fill
								(
									_timeHours
									,"0"
									,true        // from left
									,2
								)
							);
			_timeBldr.Append(":");
			_timeBldr.Append(	initrode.utilities.StringManager.Fill
								(
									_timeMinutes
									,"0"
									,true        // from left
									,2
								)
							);
			_timeBldr.Append(":");
			_timeBldr.Append(	initrode.utilities.StringManager.Fill
								(
									_timeSeconds
									,"0"
									,true        // from left
									,2
								)
							);
			if (initrode.utilities.StringManager.IsValidTimeInHHColonMIColonSSFormat(_timeBldr.ToString()) == false) 
				return new DateTime(1900,1,1);

			return new	DateTime	
						(
							Convert.ToInt32(_dateYYYY)
							,Convert.ToInt32(_dateMM)
							,Convert.ToInt32(_dateDD)
							,Convert.ToInt32(_timeHours)
							,Convert.ToInt32(_timeMinutes)
							,Convert.ToInt32(_timeSeconds)
							,Convert.ToInt32(_timeMilliseconds)
						);
		}
        public static bool stringHas3DecimalsWithThousandsCommasInRow
                            (
                                string inputString
                                , out decimal outputDecimal1
                                , out decimal outputDecimal2
                                , out decimal outputDecimal3
                            )
        {
            outputDecimal1 = (decimal) 0;
            outputDecimal2 = (decimal) 0;
            outputDecimal3 = (decimal) 0;
            ArrayList _oldParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    inputString
                    , @" "
                );
            ArrayList _parts;
            initrode.utilities.StringManager.eliminateEmptyStringsFromArrayListOfString
            (
                _oldParts
                ,out _parts
            );
            for (int _partCtr = 0;
                    _partCtr < (_parts.Count - 2);
                    _partCtr++)
            {
                if (
                        initrode.utilities.StringManager.IsValidDecimal
                        (
                            initrode.utilities.StringManager.StripWhitespace
                            (
                                _parts[_partCtr].ToString()
                            ).Replace(",", "")
                        ) == true
                        && initrode.utilities.StringManager.IsValidDecimal
                            (
                                initrode.utilities.StringManager.StripWhitespace
                                (
                                    _parts[(_partCtr + 1)].ToString()
                                ).Replace(",","")
                                    
                            ) == true
                        && initrode.utilities.StringManager.IsValidDecimal
                            (
                                initrode.utilities.StringManager.StripWhitespace
                                (
                                    _parts[(_partCtr + 2)].ToString()
                                ).Replace(",","")
                            ) == true
                   )
                {
                    outputDecimal1 = 
                        Convert.ToDecimal
                        (
                            initrode.utilities.StringManager.StripWhitespace
                            (
                                _parts[_partCtr].ToString()
                            ).Replace(",","")
                        );
                    outputDecimal2 =
                        Convert.ToDecimal
                        (
                            initrode.utilities.StringManager.StripWhitespace
                            (
                                _parts[(_partCtr + 1)].ToString()
                            ).Replace(",", "")
                        );
                    outputDecimal3 =
                        Convert.ToDecimal
                        (
                            initrode.utilities.StringManager.StripWhitespace
                            (
                                _parts[(_partCtr + 2)].ToString()
                            ).Replace(",", "")
                        );
                    return true;
                }
            }
            return false;
        }

        public static bool stringHas3CommaDelimitedNumbersInRow
                            (
                                string inputCommaDelimitedString
                                , out string outputNumericString1
                                , out string outputNumericString2
                                , out string outputNumericString3
                            )
        {
            outputNumericString1 = "";
            outputNumericString2 = "";
            outputNumericString3 = "";
            ArrayList _parts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    inputCommaDelimitedString
                    , ","
                );
            for (int _partCtr = 0;
                    _partCtr < (_parts.Count - 2);
                    _partCtr++)
            {
                if (
                        initrode.utilities.StringManager.IsValidNumber
                        (
                            _parts[_partCtr].ToString()
                        ) == true
                        && initrode.utilities.StringManager.IsValidNumber
                            (
                                _parts[(_partCtr + 1)].ToString()
                            ) == true
                        && initrode.utilities.StringManager.IsValidNumber
                            (
                                _parts[(_partCtr + 2)].ToString()
                            ) == true
                   )
                {
                    outputNumericString1 = _parts[_partCtr].ToString();
                    outputNumericString2 = _parts[(_partCtr + 1)].ToString();
                    outputNumericString3 = _parts[(_partCtr + 2)].ToString();
                    return true;
                }
            }
            return false;
        }
        public static string getBasePathGivenPhysicalPath
                                (
                                    string inputPhysicalPath
                                )
        {
            inputPhysicalPath = inputPhysicalPath.ToUpper();
            string _endOfPath =
                initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern
                (	    
                    @"^C:\\INETPUB\\WWWROOT\\(.+)$"
                    ,inputPhysicalPath
                );
            ArrayList _pathParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    _endOfPath
                    , @"\\"
                );


            if (_pathParts.Count > 0)
            {
                StringBuilder _basePathBldr = new StringBuilder();
                _basePathBldr.Append("/");
                _basePathBldr.Append(_pathParts[0].ToString());
                return _basePathBldr.ToString();
            }
            return _endOfPath;
        }
        public static bool IsValidEMailAddressPart(string inputPart)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputPart) == true) return false;
			inputPart = initrode.utilities.StringManager.StripWhitespace(inputPart);
			for (int i = 0; i < inputPart.Length; i++)
			{
				string _character = inputPart.Substring(i,1);
				if (initrode.utilities.StringManager.CHARACTERS_IN_EMAIL_ADDRESS_PART.IndexOf(_character) < 0) return false;
			}
			return true;
		}
		public static bool IsValidEMailAddressList(string inputEMailAddressList)
		{
			if (inputEMailAddressList.IndexOf(";") >= 0) 
			{
				ArrayList _emailAddresses = initrode.utilities.StringManager.splitIntoArrayList(inputEMailAddressList,";");
				for (int _emailAddressCtr = 0; _emailAddressCtr < _emailAddresses.Count; _emailAddressCtr++)
				{
					string _emailAddress = _emailAddresses[_emailAddressCtr].ToString();
					if (initrode.utilities.StringManager.IsValidEMailAddress(_emailAddress) == false) return false;
				}
			}
			else
			{
				if (inputEMailAddressList.IndexOf(",") >= 0) 
				{
					ArrayList _emailAddresses = initrode.utilities.StringManager.splitIntoArrayList(inputEMailAddressList,";");
					for (int _emailAddressCtr = 0; _emailAddressCtr < _emailAddresses.Count; _emailAddressCtr++)
					{
						string _emailAddress = _emailAddresses[_emailAddressCtr].ToString();
						if (initrode.utilities.StringManager.IsValidEMailAddress(_emailAddress) == false) return false;
					}
				}
				else
				{
					if (initrode.utilities.StringManager.IsValidEMailAddress(inputEMailAddressList) == false) return false;
				}
			}
        
			return true;
		}

		public static bool IsEmptyString(string input)
		{
			if (input == null) return true;
			input = StripWhitespace(input);
			if (input.Length == 0) return true;
			return false;
		}
		public static string convertDateTimeInSQLFormatIntoMM_DD_YYYYFormat(string inputDateInSQLFormat)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputDateInSQLFormat) == true)
				return "";
			inputDateInSQLFormat = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputDateInSQLFormat);
			ArrayList _dateAndTimeParts = initrode.utilities.StringManager.splitIntoArrayList(inputDateInSQLFormat," ");
			if (_dateAndTimeParts.Count != 3) return "";
			string _datePart = initrode.utilities.StringManager.StripWhitespaceFromEnds(_dateAndTimeParts[0].ToString());
            ArrayList _dateParts =
                initrode.utilities.StringManager.splitIntoArrayList(_datePart, "/");
            if (_dateParts.Count != 3) return "";

            StringBuilder _dateBldr =
                new StringBuilder();
            _dateBldr.AppendFormat
            (
                "{0}/{1}/{2}"
                ,   initrode.utilities.StringManager.Fill
                    (
                        initrode.utilities.StringManager.StripWhitespaceFromEnds
                        (
                            _dateParts[0].ToString()
                        )
                        , "0"
                        , true //from left
                        , 2
                    )
                ,   initrode.utilities.StringManager.Fill
                    (
                        initrode.utilities.StringManager.StripWhitespaceFromEnds
                        (
                            _dateParts[1].ToString()
                        )
                        , "0"
                        , true //from left
                        , 2
                    )
                ,   initrode.utilities.StringManager.StripWhitespaceFromEnds
                    (
                        _dateParts[2].ToString()
                    )
            );

            return _dateBldr.ToString();
		}
        public static string   ConvertDateInBESFormatToDB2Format
                                (
                                   string inputDateInBESFormat
                                )
        {
            ArrayList _dateParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    inputDateInBESFormat
                    , " "
                );
            string _month_string = 
                initrode.utilities.StringManager.StripWhitespace
                (
                    _dateParts[0].ToString()
                ).ToUpper();
            int _month_int = 0;
            switch (_month_string)
            {
                case "JAN":
                    _month_int = 1;
                    break;
                case "FEB":
                    _month_int = 2;
                    break;
                case "MAR":
                    _month_int = 3;
                    break;
                case "APR":
                    _month_int = 4;
                    break;
                case "MAY":
                    _month_int = 5;
                    break;
                case "JUN":
                    _month_int = 6;
                    break;
                case "JUL":
                    _month_int = 7;
                    break;
                case "AUG":
                    _month_int = 8;
                    break;
                case "SEP":
                    _month_int = 9;
                    break;
                case "OCT":
                    _month_int = 10;
                    break;
                case "NOV":
                    _month_int = 11;
                    break;
                case "DEC":
                    _month_int = 12;
                    break;
            }
            string _day_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _dateParts[1].ToString()
                ).Replace(",", "");
            int _day_int =
                Convert.ToInt32
                (
                    _day_string
                );
            string _year_string =
               initrode.utilities.StringManager.StripWhitespace
               (
                   _dateParts[2].ToString()
               );
            int _year_int =
                Convert.ToInt32
                (
                    _year_string
                );

            string _time_string =
               initrode.utilities.StringManager.StripWhitespace
               (
                   _dateParts[3].ToString()
               );

            ArrayList _timeParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    _time_string
                    , ":"
                );
            string _hours_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _timeParts[0].ToString()
                );
            int _hours_int =
                Convert.ToInt32
                (
                    _hours_string
                );
            string _minutes_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _timeParts[1].ToString()
                );
            int _minutes_int =
                Convert.ToInt32
                (
                    _minutes_string
                );
            string _seconds_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _timeParts[2].ToString()
                );
            int _seconds_int =
                Convert.ToInt32
                (
                    _seconds_string
                );
            string _am_or_pm_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _dateParts[4].ToString()
                );
            if (_am_or_pm_string.CompareTo("PM") == 0)
                _hours_int += 12;
            string _microSeconds_string =
                initrode.utilities.StringManager.StripWhitespace
                (
                    _dateParts[5].ToString()
                );
            int _microSeconds_int =
                Convert.ToInt32
                (
                    _microSeconds_string
                );
            StringBuilder _outputDB2DateStringBuilder =
                new StringBuilder();
            _outputDB2DateStringBuilder.AppendFormat
            (
                "{0}-{1}-{2}-{3}.{4}.{5}.{6}"
                , _year_int.ToString()
                , initrode.utilities.StringManager.Fill
                    (
                        _month_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 2
                    )
                , initrode.utilities.StringManager.Fill
                    (
                        _day_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 2
                    )
                , initrode.utilities.StringManager.Fill
                    (
                        _hours_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 2
                    )
                , initrode.utilities.StringManager.Fill
                    (
                        _minutes_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 2
                    )
                , initrode.utilities.StringManager.Fill
                    (
                        _seconds_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 2
                    )
                , initrode.utilities.StringManager.Fill
                    (
                        _microSeconds_int.ToString()
                        , "0"
                        , true //fromLeft
                        , 6
                    )
            );
            return _outputDB2DateStringBuilder.ToString();
        }
		public static bool HasCharactersMatchingRegexString(string inputRegexString, 
															string input)
		{
			input = initrode.utilities.StringManager.StripWhitespaceFromEnds(input);
			Regex pattern = new Regex(inputRegexString);
            bool _patternMatches = false;
            _patternMatches = pattern.IsMatch(input);

            return _patternMatches;
		}

		public static bool IsValidAlphanumeric(string input)
		{
			if (initrode.utilities.StringManager.IsEmptyString(input) == true) return false;
			return initrode.utilities.StringManager.HasCharactersMatchingRegexString("[^a-zA-Z0-9$]", 
																			input);
		}

        public static bool IsValidAlphanumericPlusBlanks(string input)
        {
            if (initrode.utilities.StringManager.IsEmptyString(input) == true) return false;
            return initrode.utilities.StringManager.HasCharactersMatchingRegexString("^[a-zA-Z0-9 ]+$",
                                                                            input);
        }

		public static bool IsValidName(string input)
		{
			if (initrode.utilities.StringManager.IsEmptyString(input) == true) return false;
			return initrode.utilities.StringManager.HasCharactersMatchingRegexString("^[a-zA-Z ]+$", 
																					input);
		}
		public static bool IsValidDecimal(string input)
		{
            input =
                initrode.utilities.StringManager.StripWhitespace
                (
                    input
                );
			if (initrode.utilities.StringManager.IsEmptyString(input) == true) return false;
            if (input.IndexOf("-") == 0
                || input.IndexOf("-") == (input.Length - 1))
            {
                input = input.Replace("-", "");
            }
			return initrode.utilities.StringManager.HasCharactersMatchingRegexString(@"^\d*\.?\d+$", 
																				 input);
		}

		public static bool IsValidNumber(string input)
		{
			if (initrode.utilities.StringManager.IsEmptyString(input) == true) return false;
            input = initrode.utilities.StringManager.StripWhitespace
                    (
                        input
                    );
            if (input.Substring(0, 1).CompareTo("-") == 0)
            {
                input = input.Substring(1);
            }
			return initrode.utilities.StringManager.HasCharactersMatchingRegexString(@"^\d+$", 
																				 input);
		}
		public static string ConvertDateInYYYYMMDDToMMSlashDDSlashYYYY(string inputDateInYYYYMMDDFormat)
		{
			string _strDate =  initrode.utilities.StringManager.StripWhitespace(inputDateInYYYYMMDDFormat);
			if (_strDate.Length != 8) return "";

			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(inputDateInYYYYMMDDFormat.Substring(4,2));
			_bldr.Append("/");
			_bldr.Append(inputDateInYYYYMMDDFormat.Substring(6,2));
			_bldr.Append("/");
			_bldr.Append(inputDateInYYYYMMDDFormat.Substring(0,4));
			return _bldr.ToString();
		}
		public static string	splicePathAndFileNameInfo
								(	
									string inputPath,
									string inputFileName
								)
		{
			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(inputPath);
			_bldr.Append("\\");
			_bldr.Append(inputFileName);
			return _bldr.ToString();
		}
														
		public static void ConvertLastNameCommaFirstNameToLastNameAndFirstName(
								string inputName,
								out string outputLastName,
								out string outputFirstName)
		{
			outputLastName = "";
			outputFirstName = "";
			ArrayList _parts = initrode.utilities.StringManager.splitIntoArrayList(
										inputName,
										",");
			if (_parts.Count != 2) return;
			outputLastName = initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[0].ToString());
			outputFirstName = initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[1].ToString());
		}
		public static void ConvertDashedCodeAndLastNameCommaFirstNameIntoCodeLastNameAndFirstName(
								string inputCodeLastNameFirstName,
								out string outputCode,
								out string outputLastName,
								out string outputFirstName)
		{
			outputCode = "";
			outputLastName = "";
			outputFirstName = "";

			if (inputCodeLastNameFirstName.IndexOf("-") < 0 
				|| inputCodeLastNameFirstName.IndexOf(",") < 0) return;
			string _lastNameCommaFirstName = "";
			initrode.utilities.StringManager.ConvertDashedCodeAndDescriptionToCodeAndDescription(
								inputCodeLastNameFirstName,
								out outputCode,
								out _lastNameCommaFirstName);
			ConvertLastNameCommaFirstNameToLastNameAndFirstName(
								 _lastNameCommaFirstName,
								out outputLastName,
								out outputFirstName);

		}
		public static void ConvertDashedCodeAndDescriptionToCodeAndDescription(
								string inputCodeAndDescription,
								out string outputCode,
								out string outputDescription)
		{
			outputCode = "";
			outputDescription = "";

			if (inputCodeAndDescription == null 
				|| initrode.utilities.StringManager.StripWhitespaceFromEnds(inputCodeAndDescription).Length < 3) return;	
			inputCodeAndDescription = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputCodeAndDescription);

			outputCode = initrode.utilities.StringManager.StripWhitespaceFromEnds(
							initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern(	
									@"^(.*)-.*$",
									inputCodeAndDescription));

			outputDescription = initrode.utilities.StringManager.StripWhitespaceFromEnds(
									initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern(	
										@"^.*-(.*)$",
										inputCodeAndDescription));
		}

		public static void ConvertPhoneNumberInParenthesesedAreaCodeAndDashedPrefixAndSuffixIntoParts(
								string inputPhoneNumber,
								out string outputAreaCode,
								out string outputPrefix,
								out string outputSuffix)
		{
			outputAreaCode = "";
			outputPrefix = "";
			outputSuffix = "";

			if (inputPhoneNumber == null 
				|| initrode.utilities.StringManager.StripWhitespaceFromEnds(inputPhoneNumber).Length < 13) return;	

			outputAreaCode = initrode.utilities.StringManager.StripWhitespaceFromEnds(
								initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern(	
									@"^.*\(([0-9][0-9][0-9])\).+$",
									inputPhoneNumber));

			outputPrefix = initrode.utilities.StringManager.StripWhitespaceFromEnds(
								initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern(	
									@"^.*([0-9][0-9][0-9])\w*-.*$",
									inputPhoneNumber));

			outputSuffix = initrode.utilities.StringManager.StripWhitespaceFromEnds(
							initrode.utilities.StringManager.CaptureFirstOccurrenceOfParenthesesedPattern(	
									@"^.*-([0-9][0-9][0-9][0-9]).*$",
									inputPhoneNumber));

		}

		public static double ConvertStringWithNoPeriodIntoDoubleWithTwoDecimalPlaces(string inputNumber)
		{
			inputNumber =  initrode.utilities.StringManager.StripWhitespace(inputNumber);
			if (initrode.utilities.StringManager.IsValidNumber(inputNumber) == false) return 0;

			return (Convert.ToDouble(inputNumber)/100);
		}

		public static string ConvertDateInMMSlashDDSlashYYYYToYYYYMMDD(string inputDateInMMSlashDDSlashYYYYFormat)
		{
			string _strDate =  initrode.utilities.StringManager.StripWhitespace(inputDateInMMSlashDDSlashYYYYFormat);
			if (_strDate.Length != 10 
				|| _strDate.IndexOf("/") == -1) return "";

			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(6,4));
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(0,2));
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(3,2));
			return _bldr.ToString();
		}

		public static string ConvertDateInMMSlashDDSlashYYYYToYYYYDashMMDashDD(string inputDateInMMSlashDDSlashYYYYFormat)
		{
			string _strDate =  initrode.utilities.StringManager.StripWhitespace(inputDateInMMSlashDDSlashYYYYFormat);
			if (_strDate.Length != 10 
				|| _strDate.IndexOf("/") == -1) return "";

			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(6,4));
			_bldr.Append("-");
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(0,2));
			_bldr.Append("-");
			_bldr.Append(inputDateInMMSlashDDSlashYYYYFormat.Substring(3,2));
			return _bldr.ToString();
		}

		public static void GetDateTimeIfValidDateInYYYYMMDDFormat(string inputDateInYYYYMMDDFormat,
																	out bool outputValid,
																	out DateTime outputDateTime)
		{
			outputValid = false;
			outputDateTime = new DateTime();
			string _strippedInputDate = initrode.utilities.StringManager.StripWhitespace(inputDateInYYYYMMDDFormat);
			if (_strippedInputDate.Length != 8) return;

			string _mm = _strippedInputDate.Substring(4,2);
			string _dd = _strippedInputDate.Substring(6,2);
			string _yyyy = _strippedInputDate.Substring(0,4);
			if (initrode.utilities.StringManager.IsValidDate(	_mm,
															_dd,
															_yyyy) == false) return;

			// We have a valid date - convert it to DateTime
			outputValid = true;
			outputDateTime = new DateTime(	Convert.ToInt16(_yyyy),
											Convert.ToInt16(_mm),
											Convert.ToInt16(_dd));	
	
		}
		public static bool IsValidDateInYYYYMMDDFormat(string inputDateInYYYYMMDDFormat)
		{
			inputDateInYYYYMMDDFormat = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputDateInYYYYMMDDFormat);
			if (inputDateInYYYYMMDDFormat.Length != 8) return false;
			
			string _strYYYY = inputDateInYYYYMMDDFormat.Substring(0,4);
			string _strMM = inputDateInYYYYMMDDFormat.Substring(4,2);
			string _strDD = inputDateInYYYYMMDDFormat.Substring(6,2);

			if (initrode.utilities.StringManager.IsValidDate(	_strMM,
															_strDD,
															_strYYYY) == false) return false;
			return true;
			
		}
		public static bool	IsValidTimeInHHColonMIColonSSFormat
							(
								string inputTimeInHHColonMIColonSSFormat
							)
		{
			inputTimeInHHColonMIColonSSFormat = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputTimeInHHColonMIColonSSFormat);
			if (inputTimeInHHColonMIColonSSFormat.Length != 8) return false;

			string _strHH =	inputTimeInHHColonMIColonSSFormat.Substring(0,2); 
			string _strMI =	inputTimeInHHColonMIColonSSFormat.Substring(3,2); 
			string _strSS =	inputTimeInHHColonMIColonSSFormat.Substring(6,2); 
			if (initrode.utilities.StringManager.IsValidNumber(_strHH) == false ||
				initrode.utilities.StringManager.IsValidNumber(_strMI) == false ||
				initrode.utilities.StringManager.IsValidNumber(_strSS) == false) return false;
			
			int _intHH = Convert.ToInt32(_strHH);
			int _intMI = Convert.ToInt32(_strMI);
			int _intSS = Convert.ToInt32(_strSS);
		
			if (_intHH < 0 || _intHH > 23) return false;
			if (_intMI < 0 || _intMI > 59) return false;
			if (_intSS < 0 || _intSS > 59) return false;

			return true;
		}

		public static string getCurrentDateInMMSlashDDSlashYYYYFormat()
		{
			DateTime _currentDate = DateTime.Now;
			StringBuilder _currentDateBldr = new StringBuilder();
			_currentDateBldr.Append(initrode.utilities.StringManager.Fill(
											_currentDate.Month.ToString(),
											"0",
											true,
											2));
			_currentDateBldr.Append("/");
			_currentDateBldr.Append(initrode.utilities.StringManager.Fill(
											_currentDate.Day.ToString(),
											"0",
											true,
											2));
			_currentDateBldr.Append("/");
			_currentDateBldr.Append(_currentDate.Year.ToString());
			return _currentDateBldr.ToString();
		}

		public static string getCurrentDateInYYYYMMDDFormat()
		{
			DateTime _currentDate = DateTime.Now;
			StringBuilder _currentDateBldr = new StringBuilder();
			_currentDateBldr.Append(_currentDate.Year.ToString());
			_currentDateBldr.Append(initrode.utilities.StringManager.Fill(
											_currentDate.Month.ToString(),
											"0",
											true,
											2));
			_currentDateBldr.Append(initrode.utilities.StringManager.Fill(
											_currentDate.Day.ToString(),
											"0",
											true,
											2));
			return _currentDateBldr.ToString();
		}

		public static bool IsValidTimestamp(string inputTimestamp)
		{
			if (	initrode.utilities.StringManager.IsEmptyString(inputTimestamp) == true) return false;
			if (	initrode.utilities.DateManager.date1ComparedToDate2
					(
						initrode.utilities.StringManager.ConvertTimestampStringToDateTime(inputTimestamp)
						,new DateTime(1900,1,1)
					) == 0) return false;
			return true;
		}
		public static void ValidateAndConvertDateInMM_DD_YYYYFormatWithPossibleOneDigitMonthOrDayToMM_DD_YYYYFormat(
								string inputDateInMM_DD_YYYYFormat,
								out bool outputIsValid,
								out string outputDateInMM_DD_YYYYFormat)
		{
			outputDateInMM_DD_YYYYFormat = "01/01/1900";
			outputIsValid = false;
			if (initrode.utilities.StringManager.IsEmptyString(inputDateInMM_DD_YYYYFormat) == true) return;
			ArrayList _parts = initrode.utilities.StringManager.splitIntoArrayList(
										inputDateInMM_DD_YYYYFormat,
										"/");
			if (_parts.Count != 3) return;
			string strMM = initrode.utilities.StringManager.Fill(
							initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[0].ToString()),
							"0",
							true,
							2);
			string strDD = initrode.utilities.StringManager.Fill(
							initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[1].ToString()),
							"0",
							true,
							2);
			string strYYYY = initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[2].ToString());
			if (initrode.utilities.StringManager.IsValidDate(
											strMM,
											strDD,
											strYYYY) == false) return;

			outputIsValid = true;

			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(strMM);
			_bldr.Append("/");
			_bldr.Append(strDD);
			_bldr.Append("/");
			_bldr.Append(strYYYY);

			outputDateInMM_DD_YYYYFormat = _bldr.ToString();
		}

		public static bool IsValidEMailAddress(string inputEMailAddress)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputEMailAddress) == true) return false;
			if (inputEMailAddress.IndexOf("@") < 0) return false;
			
			ArrayList _parts = initrode.utilities.StringManager.splitIntoArrayList(inputEMailAddress,"@");
			for (int _partCtr = 0; _partCtr < _parts.Count; _partCtr++)
			{
				string _part = _parts[_partCtr].ToString();
				if (initrode.utilities.StringManager.IsEmptyString(_part) == true) return false;
				if (_part.IndexOf(@".") >= 0)
				{
					ArrayList _periodDelimitedParts = initrode.utilities.StringManager.splitIntoArrayList(_part,@".");
					for (int _periodDelimitedPartCtr = 0; _periodDelimitedPartCtr < _periodDelimitedParts.Count; _periodDelimitedPartCtr++)
					{
						string _periodDelimitedPart = _periodDelimitedParts[_periodDelimitedPartCtr].ToString();
						if (initrode.utilities.StringManager.IsEmptyString(_periodDelimitedPart) == true) return false;
						if (initrode.utilities.StringManager.IsValidEMailAddressPart(_periodDelimitedPart) == false) return false;
					}
				}
				else
				{
					if (initrode.utilities.StringManager.IsValidEMailAddressPart(_part) == false) return false;
				}
			}
			return true;
		}

		public static bool IsValidDateInMM_DD_YYYYFormat(string inputDateInMM_DD_YYYYFormat)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputDateInMM_DD_YYYYFormat) == true) return false;

			if (initrode.utilities.StringManager.StripWhitespace(inputDateInMM_DD_YYYYFormat).Length != 10) return false;
			return initrode.utilities.StringManager.IsValidDate(inputDateInMM_DD_YYYYFormat.Substring(0,2),
															inputDateInMM_DD_YYYYFormat.Substring(3,2),
															inputDateInMM_DD_YYYYFormat.Substring(6,4));
			

		}
		public static bool IsValidDateInYYYYDashMMDashDDFormat(string inputDateInYYYYDashMMDashDDFormat)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputDateInYYYYDashMMDashDDFormat) == true) return false;
			inputDateInYYYYDashMMDashDDFormat = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputDateInYYYYDashMMDashDDFormat);
			if (inputDateInYYYYDashMMDashDDFormat.Length != 10) return false;

			string _strTestDateYYYY = inputDateInYYYYDashMMDashDDFormat.Substring(0,4);
			string _strTestDateMM = inputDateInYYYYDashMMDashDDFormat.Substring(5,2);
			string _strTestDateDD = inputDateInYYYYDashMMDashDDFormat.Substring(8,2);
			if (initrode.utilities.StringManager.IsValidDate(
						_strTestDateMM,
						_strTestDateDD,
						_strTestDateYYYY) == true) return true;
			return false;
		}
		public static DateTime convertDateInYYYYDashMMDashDDToDateTime(string inputDateInYYYYDashMMDashDDFormat)
		{
			inputDateInYYYYDashMMDashDDFormat = initrode.utilities.StringManager.StripWhitespaceFromEnds(inputDateInYYYYDashMMDashDDFormat);
			if (initrode.utilities.StringManager.IsValidDateInYYYYDashMMDashDDFormat(inputDateInYYYYDashMMDashDDFormat) == true)
			{
				string _strTestDateYYYY = inputDateInYYYYDashMMDashDDFormat.Substring(0,4);
				string _strTestDateMM = inputDateInYYYYDashMMDashDDFormat.Substring(5,2);
				string _strTestDateDD = inputDateInYYYYDashMMDashDDFormat.Substring(8,2);
				int _intTestDateYYYY = Convert.ToInt32(_strTestDateYYYY);
				int _intTestDateMM = Convert.ToInt32(_strTestDateMM);
				int _intTestDateDD = Convert.ToInt32(_strTestDateDD);
				return new DateTime(_intTestDateYYYY,_intTestDateMM,_intTestDateDD);
			}
			return new DateTime(DateTime.Now.Year,
								DateTime.Now.Month,
								DateTime.Now.Day);
		}
		public static bool IsValidDate(string inputMM,
			string inputDD,
			string inputYYYY)
		{
			if (initrode.utilities.StringManager.IsValidNumber(inputMM) == false ||
				initrode.utilities.StringManager.IsValidNumber(inputDD) == false ||
				initrode.utilities.StringManager.IsValidNumber(inputYYYY) == false) return false;

			int _MM = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputMM);
			int _DD = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputDD);
			int _YYYY = initrode.utilities.StringManager.ConvertCharacterStringToInt(inputYYYY);

			if (_MM < 1 || _MM > 12) return false;

			if (_MM == 1 || 
				_MM == 3 || 
				_MM == 5 || 
				_MM == 7 || 
				_MM == 8 || 
				_MM == 10 || 
				_MM == 12)
			{
				if (_DD < 1 || _DD > 31) return false;
			}
				
			if (_MM == 4 || 
				_MM == 6 || 
				_MM == 9 ||
				_MM == 11)
			{
				if (_DD < 1 || _DD > 30) return false;
			}

			if (_MM == 2)
			{
				bool doesFebruaryHave29Days = false;
				if (_YYYY % 4 == 0)
				{
					if (_YYYY % 100 == 0)
					{
						if (_YYYY % 400 == 0)
						{
							doesFebruaryHave29Days = true;
						}
					}
					else
					{
						doesFebruaryHave29Days = true;
					}
				}
				if (doesFebruaryHave29Days == true)
				{
					if (_DD < 1 || _DD > 29) return false;
				}
				else
				{
					if (_DD < 1 || _DD > 28) return false;
				}
			}
			return true;
		}
		public static ArrayList splitIntoArrayListAfterRemovingCharacterSequence(
											string inputString,
                                            string inputSplitAt,
											string inputCharacterSequenceToBeRemoved)
		{
			inputString = inputString.Replace(inputCharacterSequenceToBeRemoved,"");
			return splitIntoArrayList(inputString,
                                      inputSplitAt);
		}
		public static string	getCurrentDateStringInYYYYMMDDFormat()
		{
			StringBuilder _dateStringbldr = new StringBuilder();
			DateTime _currentDateTime = DateTime.Now;

			_dateStringbldr.Append	(_currentDateTime.Year.ToString());
			_dateStringbldr.Append	(	initrode.utilities.StringManager.Fill
										(
											_currentDateTime.Month.ToString(),
											"0",
											true,
											2
										)
									);								
			_dateStringbldr.Append	(	initrode.utilities.StringManager.Fill
										(
											_currentDateTime.Day.ToString(),
											"0",
											true,
											2
										)
									);					
			return _dateStringbldr.ToString();			
		}
		public static string buildPathAndFileNameFromPathAndFileName
							(
								string inputPath,
								string inputFileNameAndExtension
							)
		{
			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(inputPath);
			_bldr.Append("\\");
			_bldr.Append(inputFileNameAndExtension);
			return _bldr.ToString();
		}
		public static string extractFileNameFromFileNameAndExtension(string inputFileNameAndExtension)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputFileNameAndExtension) == true) return "";
			ArrayList _fileNameParts = initrode.utilities.StringManager.splitIntoArrayList(inputFileNameAndExtension,@".");
			StringBuilder returnFileNameBldr = new StringBuilder();
			for (int _partCtr = 0; _partCtr < (_fileNameParts.Count - 1); _partCtr++)
			{
				if (returnFileNameBldr.Length > 0) returnFileNameBldr.Append(".");
				returnFileNameBldr.Append(_fileNameParts[_partCtr].ToString());
			}
			return returnFileNameBldr.ToString();
			
		}
		public static string extractPathFromPathAndFileName(string inputPathAndFileName)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputPathAndFileName) == true) return "";
			ArrayList _fileNameAndPathParts = initrode.utilities.StringManager.splitIntoArrayList(inputPathAndFileName,@"\\");
			StringBuilder returnPathBldr = new StringBuilder();
			for (int _partCtr = 0; _partCtr < (_fileNameAndPathParts.Count - 1); _partCtr++)
			{
				if (returnPathBldr.Length > 0) returnPathBldr.Append("\\");
				returnPathBldr.Append(_fileNameAndPathParts[_partCtr].ToString());
			}
			return returnPathBldr.ToString();
		}

		public static string extractFileNameFromPathAndFileName(string inputPathAndFileName)
		{
			if (initrode.utilities.StringManager.IsEmptyString(inputPathAndFileName) == true) return "";
			ArrayList _fileNameAndPathParts = initrode.utilities.StringManager.splitIntoArrayList(inputPathAndFileName,@"\\");
			return _fileNameAndPathParts[(_fileNameAndPathParts.Count - 1)].ToString();
		}
        public static void eliminateEmptyStringsFromArrayListOfString
                            (
                                ArrayList inputArrayListOfStrings
                                , out ArrayList outputArrayListOfStrings
                            )
        {
            outputArrayListOfStrings = new ArrayList();
            for (int _entryCtr = 0;
                    _entryCtr < inputArrayListOfStrings.Count;
                    _entryCtr++)
            {
                if (
                        initrode.utilities.StringManager.IsEmptyString
                        (
                            inputArrayListOfStrings[_entryCtr].ToString()
                        ) == false
                    )
                    outputArrayListOfStrings.Add
                    (
                        initrode.utilities.StringManager.StripWhitespaceFromEnds
                        (
                            inputArrayListOfStrings[_entryCtr].ToString()
                        )
                    );
            }
        }
        public static ArrayList splitIntoArrayList(string inputString,
                                                   string inputSplitAt)
		{
			ArrayList _returnParts = new ArrayList();
			Regex _r;

			string[] _strParts;
			if (inputSplitAt.CompareTo(@".") == 0) _r = new Regex(@"(\.)");
			else
			{
				if (inputSplitAt.CompareTo(@"\\") == 0) _r = new Regex(@"(\\)");
				else
				{
				
					StringBuilder _bldrPattern = new StringBuilder();
					_bldrPattern.Append("(");
					_bldrPattern.Append(inputSplitAt);
					_bldrPattern.Append(")");
					_r = new Regex(_bldrPattern.ToString()); // Split on inputSplitAt.
				}
			}


			_strParts = _r.Split(inputString);
			for(int i = 0; i < _strParts.Length; i++)
			{
				string _loopPart = _strParts[i].ToString();
				if (_loopPart.CompareTo(inputSplitAt) == 0
					|| (inputSplitAt.CompareTo(@"\\") == 0
						&& _loopPart.CompareTo(@"\") == 0)) continue;
				_returnParts.Add(_strParts[i].ToString());
			}
			return _returnParts;
		}
        public static ArrayList splitIntoArrayListOfNonBlankStrings
                                (
                                    string inputString
                                    ,string inputSplitAt
                                )
        {
            ArrayList _returnParts = new ArrayList();
            Regex _r;

            string[] _strParts;
            if (inputSplitAt.CompareTo(@".") == 0) _r = new Regex(@"(\.)");
            else
            {
                if (inputSplitAt.CompareTo(@"\\") == 0) _r = new Regex(@"(\\)");
                else
                {

                    StringBuilder _bldrPattern = new StringBuilder();
                    _bldrPattern.Append("(");
                    _bldrPattern.Append(inputSplitAt);
                    _bldrPattern.Append(")");
                    _r = new Regex(_bldrPattern.ToString()); // Split on inputSplitAt.
                }
            }


            _strParts = _r.Split(inputString);
            for (int i = 0; i < _strParts.Length; i++)
            {
                string _loopPart = 
                    initrode.utilities.StringManager.StripWhitespaceFromEnds
                    (
                        _strParts[i].ToString()
                    );

                if (initrode.utilities.StringManager.IsEmptyString
                    (
                        _loopPart
                    ) == true
                    || _loopPart.CompareTo(inputSplitAt) == 0
                    || (inputSplitAt.CompareTo(@"\\") == 0
                        && _loopPart.CompareTo(@"\") == 0)) continue;
                _returnParts.Add(_strParts[i].ToString());
            }
            return _returnParts;
        }

		public static string	stripGivenCharactersFromLeftEnd
								(
									string inputStringToStrip,
									string inputStripCharacters
								)
		{
			string tempString = inputStringToStrip;
			for(int i=0; i < tempString.Length; i++)
			{
				string loopChar = tempString.Substring(i,1);
				if (inputStripCharacters.IndexOf(loopChar) < 0)
				{
					return tempString.Substring(i);
				}
			}
			return "";
		}

		public static string suppressLeadingZeroesFromScaleTwoDecimalInStringFormat(string inputDecimalInStringFormat)
		{
			string _strDecimal = initrode.utilities.StringManager.StripWhitespace(inputDecimalInStringFormat);
			string _strSign = "";

			if (_strDecimal.Substring(0,1).CompareTo("-") == 0)
			{
				_strSign = "-";	
				_strDecimal = _strDecimal.Substring(1);
			}

			ArrayList _strDecimalParts = new ArrayList();
			if (_strDecimal.IndexOf(".") >= 0)
				_strDecimalParts = initrode.utilities.StringManager.splitIntoArrayList(_strDecimal, ".");
			else
			{
				_strDecimalParts.Add(_strDecimal);	
				_strDecimalParts.Add("00");
			}
			string _strWholePart = initrode.utilities.StringManager.StripWhitespace(_strDecimalParts[0].ToString());
			string _strDecimalPart = initrode.utilities.StringManager.StripWhitespace(_strDecimalParts[1].ToString());

			// Suppress leading zeroes in whole part.			
			_strWholePart = initrode.utilities.StringManager.stripGivenCharactersFromLeftEnd
							(
								_strWholePart,
								"0"                // Characters to strip
							);
			if (_strWholePart.Length == 0)
				_strWholePart = "0";
			StringBuilder _bldr = new StringBuilder();
			_bldr.Append(_strSign);
			_bldr.Append(_strWholePart);
			_bldr.Append(".");
			_bldr.Append(_strDecimalPart);
			return _bldr.ToString();
		}

		public static string FormatSystemDateToDateString(string input)
		{
			StringBuilder bldr = new StringBuilder();
			if (initrode.utilities.StringManager.IsEmptyString(input) == true)
			{
				return "";
			}
			string[] _dateTimeParts;
			Regex _r = new Regex("( )"); // Split on spaces.
			_dateTimeParts = _r.Split(input);

			if (_dateTimeParts.Length != 5)
			{
				return "";
			}

			string[] _dateParts;
			Regex _r2 = new Regex("(/)"); // Split on spaces.
			_dateParts = _r2.Split(_dateTimeParts[0].ToString());

			if (_dateParts.Length != 5)
			{
				return "";
			}
			bldr.Append(initrode.utilities.StringManager.Fill(_dateParts[0].ToString(),
															"0",
															true,
															2));
			bldr.Append("/");
			bldr.Append(initrode.utilities.StringManager.Fill(_dateParts[2].ToString(),
															"0",
															true,
															2));
			bldr.Append("/");
			bldr.Append(_dateParts[4].ToString());

			return bldr.ToString();
		}

		public static string Fill(string input,
			string fillChars,
			bool boolFromLeft,
			int maxLength)
		{
			input = initrode.utilities.StringManager.StripWhitespaceFromEnds(input);
			StringBuilder bldr = new StringBuilder("",200);
			switch(boolFromLeft)
			{
				case true:
					while((input.Length + bldr.Length + fillChars.Length) <= maxLength)
					{
						bldr.Append(fillChars);
					}
					bldr.Append(input);
					break;
				case false:
					bldr.Append(input);
					while((bldr.Length + fillChars.Length) <= maxLength)
					{
						bldr.Append(fillChars);
					}
					break;
			}
			return bldr.ToString();
		}


		public static int ConvertCharacterStringToInt(string input)
		{
			string numericCharacters = "0123456789";
			input = initrode.utilities.StringManager.StripWhitespace(input);
			StringBuilder bldr = new StringBuilder("",10);
			for(int i=0; i < input.Length; i++)
			{
				string loopChar = input.Substring(i,1);
				if (numericCharacters.IndexOf(loopChar) >= 0)
				{
					bldr.Append(loopChar);
				}
			}
			if (bldr.Length == 0) return 0;
			return Convert.ToInt32(bldr.ToString());
		}

		public static string UpperCaseFirstCharactersInName(string input)
		{
			input = initrode.utilities.StringManager.StripWhitespaceFromEnds(input);
			char[] _splitChars = new char[1];
			_splitChars[0] = ' ';
			string[] _parts = input.Split(_splitChars);
			StringBuilder _bldr = new StringBuilder();
			for(int _partCtr = 0; _partCtr < _parts.Length; _partCtr++)
			{
				string _part = initrode.utilities.StringManager.StripWhitespaceFromEnds(_parts[_partCtr].ToString().ToLower());
				if (initrode.utilities.StringManager.IsEmptyString(_part) == true) continue;

				_bldr.Append(_part.Substring(0,1).ToUpper());
				if (_part.Length > 1)
				{
					_bldr.Append(_part.Substring(1));
				}
				if (_partCtr < (_parts.Length - 1))
				{
					_bldr.Append(" ");
				}
			}
			return _bldr.ToString();
		}
		public static string GetCurrentDateInYYYY_MM_DD_FORMAT()
		{
			StringBuilder _bldr = new StringBuilder();
			DateTime _currentDate = DateTime.Now;
			_bldr.Append(_currentDate.Year.ToString());
			_bldr.Append("-");
			_bldr.Append(initrode.utilities.StringManager.Fill(
							_currentDate.Month.ToString(),
							"0",    /* Zero-fill */
							true,   /* From Left */
							2));
			_bldr.Append("-");
			_bldr.Append(initrode.utilities.StringManager.Fill(
							_currentDate.Day.ToString(),
							"0",    /* Zero-fill */
							true,   /* From Left */
							2));
			return _bldr.ToString();
		}
		public static string StripWhitespaceFromEnds(string input)
		{
			string whitespaceCharacters = " \n\t\r";
			string tempString = input;
			if (initrode.utilities.StringManager.IsEmptyString(tempString) == true) return "";
			tempString = input;
			
			for(int i=0; i < tempString.Length; i++)
			{
				string loopChar = tempString.Substring(i,1);
				if (whitespaceCharacters.IndexOf(loopChar) < 0)
				{
					tempString = tempString.Substring(i);
					break;
				}
			}
			for(int i=(tempString.Length - 1); i >= 0; i--)
			{
				string loopChar = tempString.Substring(i,1);
				if (whitespaceCharacters.IndexOf(loopChar) < 0)
				{
					tempString = tempString.Substring(0,i+1);
					break;
				}
			}
			return tempString;
		}
		public static string StripWhitespace(string input)
		{
			string whitespaceCharacters = " \n\t\r";
			string tempString = input;
			StringBuilder bldr = new StringBuilder();
			for(int i=0; i < tempString.Length; i++)
			{
                if (i >= tempString.Length)
                    return bldr.ToString();
				string loopChar = tempString.Substring(i,1);
				if (whitespaceCharacters.IndexOf(loopChar) < 0)
				{
					bldr.Append(loopChar);
				}
			}
			return bldr.ToString();
		}
		public static string CaptureFirstOccurrenceOfParenthesesedPattern(	string inputPattern,
																			string inputStringToSearch)
		{
			string _returnString = "";				
			// Compile the regular expression.
			Regex _r = new Regex(inputPattern, RegexOptions.IgnoreCase);
			// Match the regular expression pattern against a text string.
			Match _m = _r.Match(inputStringToSearch);
			if (_m.Success) 
			{
				// 1 refers to the first parentheses'd match found.
				Group _g = _m.Groups[1];
				CaptureCollection _cc = _g.Captures;
				_returnString = initrode.utilities.StringManager.StripWhitespaceFromEnds(_cc[0].ToString().ToUpper());
			}
			return _returnString;
		}

		public static string GetDisplayFormatFor10DigitPhoneNumber(string inputPhoneNumber)
		{
			string _phoneNumber = initrode.utilities.StringManager.StripWhitespace(inputPhoneNumber);
			if (_phoneNumber.Length != 10)
			{
				return string.Empty;
			}
			StringBuilder _returnPhoneNumberBldr = new StringBuilder();
			_returnPhoneNumberBldr.Append("(");
			_returnPhoneNumberBldr.Append(_phoneNumber.Substring(0,3));
			_returnPhoneNumberBldr.Append(") ");
			_returnPhoneNumberBldr.Append(_phoneNumber.Substring(3,3));
			_returnPhoneNumberBldr.Append(" - ");
			_returnPhoneNumberBldr.Append(_phoneNumber.Substring(6,4));
			return _returnPhoneNumberBldr.ToString();
		}
        public static string convertIntegerToStringWithThousandsDelimiters
                                (
                                      int inputDecimal
                                )
        {
            StringBuilder _integerBldr = new StringBuilder();
            string _rest = inputDecimal.ToString();
            while (_rest.Length >= 4)
            {
                _integerBldr.AppendFormat
                (
                    ",{0}"
                    , _rest.Substring((_rest.Length - 3), 3)
                );
                _rest = _rest.Substring(0, (_rest.Length - 3));
            }
            _integerBldr.Insert
            (
                0
                , _rest
            );
            return _integerBldr.ToString();
        }
        public static string    convertDecimalToStringWithThousandsDelimiters
                                (
                                    decimal inputDecimal
                                    , int inputNumberOfDecimalPlaces
                                )
        {
            string _workString = inputDecimal.ToString();
            ArrayList _workStringParts =
                initrode.utilities.StringManager.splitIntoArrayList
                (
                    _workString
                    ,@"."
                );
            string _wholePart = "";
            string _decimalPart = "";
            if (_workStringParts.Count > 2) return "";
            if (_workStringParts.Count == 1)
            {
                _wholePart = _workStringParts[0].ToString();
                _decimalPart = "00";
            }
            else
            {
                _wholePart = _workStringParts[0].ToString();
                _decimalPart = _workStringParts[1].ToString();
            }


            _decimalPart =
                initrode.utilities.StringManager.Fill
                (
                    _decimalPart
                    , "0"
                    , true  //Fill from left
                    , inputNumberOfDecimalPlaces
                );
            StringBuilder _decimalBldr = new StringBuilder();
            string _rest = _wholePart;
            while (_rest.Length >= 4)
            {
                _decimalBldr.AppendFormat
                (
                    ",{0}"
                    , _rest.Substring((_rest.Length - 3), 3)
                );
                _rest = _rest.Substring(0, (_rest.Length - 3));
            }
            _decimalBldr.Insert
            (
                0
                ,_rest
            );
            _decimalBldr.AppendFormat
            (
                ".{0}"
                ,_decimalPart
            );
            return _decimalBldr.ToString();			
        }
		public static string	ConvertDecimalToMainframeStringOfDigitsWithoutDecimalPoint
								(
									decimal inputDecimal
									, int inputNumberOfMainframeDigitsUsed
								)
		{
			string _workString = inputDecimal.ToString();
			_workString.Replace(".","");
			_workString = 
				initrode.utilities.StringManager.Fill
				(
					_workString
					,"0" 
					,true  //Fill from left
					,inputNumberOfMainframeDigitsUsed
				);		
			return _workString;			
		}
	}
}

Electric Geek Transportation Systems

By Jeff Atwood

I've never thought of myself as a "car person". The last new car I bought (and in fact, now that I think about it, the first new car I ever bought) was the quirky 1998 Ford Contour SVT. Since then we bought a VW station wagon in 2011

CodeSOD: I'm Sooooooo Random, LOL

By Remy Porter

There are some blocks of code that require a preamble, and an explanation of the code and its flow. Often you need to provide some broader context.

Sometimes, you get some code like Wolf found, which needs no explanation:

export function generateRandomId(): string { counter++; return 'id' + counter; }

I mean, I guess that's a slightly better than this solution. Wolf found this because some code downstream was expecting random, unique IDs, and wasn't getting them.

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

Lowest Bidder Squared

By Jane Bailey

Stack of coins 0214

Initech was in dire straits. The website was dog slow, and the budget had been exceeded by a factor of five already trying to fix it. Korbin, today's submitter, was brought in to help in exchange for decent pay and an office in their facility.

He showed up only to find a boxed-up computer and a brand new flat-packed desk, also still in the box. The majority of the space was a video-recording studio that saw maybe 4-6 hours of use a week. After setting up his office, Korbin spent the next day and a half finding his way around the completely undocumented C# code. The third day, there was a carpenter in the studio area. Inexplicably, said carpenter decided he needed to contact-glue carpet to a set of huge risers ... indoors. At least a gallon of contact cement was involved. In minutes, Korbin got a raging headache, and he was essentially gassed out of the building for the rest of the day. Things were not off to a good start.

Upon asking around, Korbin quickly determined that the contractors originally responsible for coding the website had underbid the project by half, then subcontracted the whole thing out to a team in India to do the work on the cheap. The India team had then done the very same thing, subcontracting it out to the most cut-rate individuals they could find. Everything had been written in triplicate for some reason, making it impossible to determine what was actually powering the website and what was dead code. Furthermore, while this was a database-oriented site, there were no stored procedures, and none of the (sub)subcontractors seemed to understand how to use a JOIN command.

In an effort to tease apart what code was actually needed, Korbin turned on profiling. Only ... it was already on in the test version of the site. With a sudden ominous hunch, he checked the live site—and sure enough, profiling was running in production as well. He shut it off, and instantly, the whole site became more responsive.

The next fix was also pretty simple. The site had a bad habit of asking for information it already had, over and over, without any JOINs. Reducing the frequency of database hits improved performance again, bringing it to within an order of magnitude of what one might expect from a website.

While all this was going on, the leaderboard page had begun timing out. Sure enough, it was an N-squared solution: open database, fetch record, close database, repeat, then compare the two records, putting them in order and beginning again. With 500 members, it was doing 250,000 passes each time someone hit the page. Korbin scrapped the whole thing in favor of the site's first stored procedure, then cached it to call only once a day.

The weeks went on, and the site began to take shape, finally getting something like back on track. Thanks to the botched rollout, however, many of the company's endorsements had vanished, and backers were pulling out. The president got on the phone with some VIP about Facebook—because as we all know, the solution to any company's problem is the solution to every company's problems.

"Facebook was written in PHP. He told me it was the best thing out there. So we're going to completely redo the website in PHP," the president confidently announced at the next all-hands meeting. "I want to hear how long everyone thinks this will take to get done."

The only developers left at that point were Korbin and a junior kid just out of college, with one contractor with some experience on the project.

"Two weeks. Maybe three," the kid replied.

They went around the table, and all the non-programmers chimed in with the 2-3 week assessment. Next to last came the experienced contractor. Korbin's jaw nearly dropped when he weighed in at 3-4 weeks.

"None of that is realistic!" Korbin proclaimed. "Even with the existing code as a road map, it's going to take 4-6 months to rewrite. And with the inevitable feature-creep and fixes for things found in testing, it is likely to take even longer."

Korbin was told the next day he could pick up his final check. Seven months later, he ran into the junior kid again, and asked how the rewrite went.

"It's still ongoing," he admitted.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

Error'd: What About the Fish?

By Mark Bowytz

"On the one hand, I don't want to know what the fish has to do with Boris Johnson's love life...but on the other hand I have to know!" Mark R. writes.

 

"Not sure if that's a new GDPR rule or the Slack Mailbot's weekend was just that much better then mine," Adam G. writes.

 

Connor W. wrote, "You know what, I think I'll just stay inside."

 

"It's great to see that an attempt at personalization was made, but whatever happened to 'trust but verify'?" writes Rob H.

 

"For a while, I thought that, maybe, I didn't actually know how to use my iPhone's alarm. Instead, I found that it just wasn't working right. So, I contacted Apple Support, and while they were initially skeptical that it was an iOS issue, this morning, I actually have proof!" Markus G. wrote.

 

Tim G. writes, "I guess that's better than an angry error message."

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!

CodeSOD: A Devil With a Date

By Remy Porter

Jim was adding a feature to the backend. This feature updated a few fields on an object, and then handed the object off as JSON to the front-end.

Adding the feature seemed pretty simple, but when Jim went to check out its behavior in the front-end, he got validation errors. Something in the data getting passed back by his web service was fighting with the front end.

On its surface, that seemed like a reasonable problem, but when looking into it, Jim discovered that it was the record_update_date field which was causing validation issues. The front-end displayed this as a read only field, so there was no reason to do any client-side validation in the first place, and that field was never sent to the backend, so there was even less than no reason to do validation.

Worse, the field had, at least to the eye, a valid date: 2019-07-29T00:00:00.000Z. Even weirder, if Jim changed the backend to just return 2019-07-29, everything worked. He dug into the validation code to see what might be wrong about it:

/**
 * Custom validation
 *
 * This is a callback function for ajv custom keywords
 *
 * @param  {object} wsFormat aiFormat property content
 * @param  {object} data Data (of element type) from document where validation is required
 * @param  {object} itemSchema Schema part from wsValidation keyword
 * @param  {string} dataPath Path to document element
 * @param  {object} parentData Data of parent object
 * @param  {string} key Property name
 * @param  {object} rootData Document data
 */
function wsFormatFunction(wsFormat, data, itemSchema, dataPath, parentData, key, rootData) {

    let valid;
    switch (aiFormat) {
        case 'date': {
            let regex = /^\d\d\d\d-[0-1]\d-[0-3](T00:00:00.000Z)?\d$/;
            valid = regex.test(data);
            break;
        }
        case 'date-time': {
            let regex = /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i;
            valid = regex.test(data);
            break;
        }
        case 'time': {
            let regex = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/;
            valid = regex.test(data);
            break;
        }
        default: throw 'Unknown wsFormat: ' + wsFormat;
    }

    if (!valid) {
        wsFormatFunction['errors'] = wsFormatFunction['errors'] || [];

        wsFormatFunction['errors'].push({
            keyword: 'wsFormat',
            dataPath: dataPath,
            message: 'should match format "' + wsFormat + '"',
            schema: itemSchema,
            data: data
        });
    }

    return valid;
}

When it starts with “Custom validation” and it involves dates, you know you’re in for a bad time. Worse, it’s custom validation, dates, and regular expressions written by someone who clearly didn’t understand regular expressions.

Let’s take a peek at the branch which was causing Jim’s error, and examine the regex:

/^\d\d\d\d-[0-1]\d-[0-3](T00:00:00.000Z)?\d$/

It should start with four digits, followed by a dash, followed by a value between 0 and 1. Then another digit, then a dash, then a number between 0 and 3, then the time (optionally), then a final digit.

It’s obvious why Jim’s perfectly reasonable date wasn’t working: it needed to be 2019-07-2T00:00:00.000Z9. Or, if Jim just didn’t include the timestamp, not only would 2019-07-29 be a valid date, but so would 2019-19-39, which just so happens to be my birthday. Mark your calendars for the 39th of Undevigintiber.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

CodeSOD: A Loop in the String

By Remy Porter

Robert was browsing through a little JavaScript used at his organization, and found this gem of type conversion.

//use only for small numbers
function StringToInteger (str) {
    var int = -1;
    for (var i=0; i<=100; i++) {
        if (i+"" == str) {
            int = i;
            break;
        }
    }
    return int;
}

So, this takes our input str, which is presumably a string, and it starts counting from 0 to 100. i+"" coerces the integer value to a string, which we compare against our string. If it’s a match, we’ll store that value and break out of the loop.

Obviously, this has a glaring flaw: the 100 is hardcoded. So what we really need to do is add a search_low and search_high parameter, so we can write the for loop as i = search_low; i <= search_high; i++ instead. Because that’s the only glaring flaw in this code. I can’t think of any possible better way of converting strings to integers. Not a one.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

CodeSOD: Nullable Knowledge

By Remy Porter

You’ve got a decimal value- maybe. It could be nothing at all, and you need to handle that null gracefully. Fortunately for you, C# has “nullable types”, which make this task easy.

Ian P’s co-worker made this straightforward application of nullable types.

public static decimal ValidateDecimal(decimal? value)
{
if (value == null) return 0;
decimal returnValue = 0;
Decimal.TryParse(value.ToString(), out returnValue);
return returnValue;
}

The lack of indentation was in the original.

The obvious facepalm is the Decimal.TryParse call. If our decimal has a value, we could just return it, but no, instead, we convert it to a string then convert that string back into a Decimal.

But the real problem here is someone who doesn’t understand what .NET’s nullable types offer. For starters, one could make the argument that value.HasValue() is more readable than value == null, though that’s clearly debatable. That’s not really the problem though.

The purpose of ValidateDecimal is to return the input value, unless the input value was null, in which case we want to return 0. Nullable types have a lovely GetValueOrDefault() method, which returns the value, or a reasonable default. What is the default for any built in numeric type?

0.

This method doesn’t need to exist, it’s already built in to the decimal? type. Of course, the built-in method almost certainly doesn’t do a string conversion to get its value, so the one with a string is better, is it knot?

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!

Internship of Things

By Remy Porter

Mindy was pretty excited to start her internship with Initech's Internet-of-Things division. She'd been hearing at every job fair how IoT was still going to be blowing up in a few years, and how important it would be for her career to have some background in it.

It was a pretty standard internship. Mindy went to meetings, shadowed developers, did some light-but-heavily-supervised changes to the website for controlling your thermostat/camera/refrigerator all in one device.

As part of testing, Mindy created a customer account on the QA environment for the site. She chucked a junk password at it, only to get a message: "Your password must be at least 8 characters long, contain at least three digits, not in sequence, four symbols, at least one space, and end with a letter, and not be more than 10 characters."

"Um, that's quite the password rule," Mindy said to her mentor, Bob.

"Well, you know how it is, most people use one password for every site, and we don't want them to do that here. That way, when our database leaks again, it minimizes the harm."

"Right, but it's not like you're storing the passwords anyway, right?" Mindy said. She knew that even leaked hashes could be dangerous, but good salting/hashing would go a long way.

"Of course we are," Bob said. "We're selling web connected thermostats to what can be charitably called 'twelve-o-clock flashers'. You know what those are, right? Every clock in their house is flashing twelve?" Bob sneered. "They can't figure out the site, so we often have to log into their account to fix the things they break."

A few days later, Initech was ready to push a firmware update to all of the Model Q baby monitor cameras. Mindy was invited to watch the process so she could understand their workflow. It started off pretty reasonable: their CI/CD system had a verified build, signed off, ready to deploy.

"So, we've got a deployment farm running in the cloud," Bob explained. "There are thousands of these devices, right? So we start by putting the binary up in an S3 bucket." Bob typed a few commands to upload the binary. "What's really important for our process is that it follows this naming convention. Because the next thing we're going to do is spin up a half dozen EC2 instances- virtual servers in the cloud."

A few more commands later, and then Bob had six sessions open to cloud servers in tmux. "Now, these servers are 'clean instances', so the very first thing I have to do is upload our SSH keys." Bob ran an ssh-copy-id command to copy the SSH key from his computer up to the six cloud VMs.

"Wait, you're using your personal SSH keys?"

"No, that'd be crazy!" Bob said. "There's one global key for every one of our Model Q cameras. We've all got a copy of it on our laptops."

"All… the developers?"

"Everybody on the team," Bob said. "Developers to management."

"On their laptops?"

"Well, we were worried about storing something so sensitive on the network."

Bob continued the process, which involved launching a script that would query a webservice to see which Model Q cameras were online, then sshing into them, having them curl down the latest firmware, and then self-update. "For the first few days, we leave all six VMs running, but once most of them have gotten the update, we'll just leave one cloud service running," Bob explained. "Helps us manage costs."

It's safe to say Mindy learned a lot during her internship. Mostly, she learned, "don't buy anything from Initech."

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!

Error'd: Intentionally Obtuse

By Mark Bowytz

"Normally I do pretty well on the Super Quiz, but then they decided to do it in Latin," writes Mike S.

 

"Uh oh, this month's AWS costs are going to be so much higher than last month's!" Ben H. writes.

 

Amanda C. wrote, "Oh, neat, Azure has some recommendations...wait...no...'just kidding' I guess?"

 

"Here I never thought that SQL Server log space could go negative, and yet, here we are," Michael writes.

 

"I love the form factor on this motherboard, but I'm not sure what case to buy with it," Todd C. writes, "Perhaps, if it isn't working, I can just give it a little kick?"

 

Maarten C. writes, "Next time, I'll name my spreadsheets with dog puns...maybe that'll make things less ruff."

 

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

CodeSOD: Swimming Downstream

By Remy Porter

When Java added their streams API, they brought the power and richness of functional programming styles to the JVM, if we ignore all the other non-Java JVM languages that already did this. Snark aside, streams were a great addition to the language, especially if we want to use them absolutely wrong.

Like this code Miles found.

See, every object in the application needs to have a unique identifier. So, for every object, there’s a method much like this one:

/**
     * Get next priceId
     *
     * @return next priceId
     */
    public String createPriceId() {
        List<String> ids = this.prices.stream().map(m -> m.getOfferPriceId()).collect(Collectors.toList());
        for (Integer i = 0; i < ids.size(); i++) {
            ids.set(i, ids.get(i).split("PR")[1]);
        }
        try {
            List<Integer> intIds = ids.stream().map(id -> Integer.parseInt(id)).collect(Collectors.toList());
            Integer max = intIds.stream().mapToInt(id -> id).max().orElse(0);
            return "PR" + (max + 1);
        } catch (Exception e) {
            return "PR" + 1;
        }
    }

The point of a stream is that you can build a processing pipeline: starting with a list, you can perform a series of operations but only touch each item in the stream once. That, of course, isn’t what we do here.

First, we map the prices to extract the offerPriceId and convert it into a list. Now, this list is a set of strings, so we iterate across that list of IDs, to break the "PR" prefix off. Then, we’ll map that list of IDs again, to parse the strings into integers. Then, we’ll cycle across that new list one more time, to find the max value. Then we can return a new ID.

And if anything goes wrong in this process, we won’t complain. We just return an ID that’s likely incorrect- "PR1". That’ll probably cause an error later, right? They can deal with it then.

Everything here is just wrong. This is the wrong way to use streams- the whole point is this could have been a single chain of function calls that only needed to iterate across the input data once. It’s also the wrong way to handle exceptions. And it’s also the wrong way to generate IDs.

Worse, a largely copy/pasted version of this code, with the names and prefixes changed, exists in nearly every model class. And these are database model classes, according to Miles, so one has to wonder if there might be a better way to generate IDs…

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

CodeSOD: Seven First Dates

By Remy Porter

Your programming language is PHP, which represents datetimes as milliseconds since the epoch. Your database, on the other hand, represents datetimes as seconds past the epoch. Now, your database driver certainly has methods to handle this, but can you really trust that?

Nancy found some code which simply needs to check: for the past week, how many customers arrived each day?

$customerCount = array();
$result2 = array();
$result3 = array();
$result4 = array();

$min = 20;
$max = 80;

for ( $i = $date; $i < $date + $days7 + $day; $i += $day ) {

	$first_datetime = date('Y-m-d H:i',substr($i - $day,0,-3));
	$second_datetime = date('Y-m-d H:i',substr($i,0,-3));

	$sql = $mydb ->prepare("SELECT 
								COUNT(DISTINCT Customer.ID) 'Customer'
				            FROM Customer
				                WHERE Timestamp BETWEEN %s AND %s",$first_datetime,$second_datetime);
	$output = $mydb->get_row($sql);
	array_push( $customerCount, $output->Customer == null ? 0 : $output->Customer);
}

array_push( $result4, $customerCount );
array_push( $result4, $result2 );
array_push( $result4, $result3 );

return $result4;

If you have a number of milliseconds and you wish to convert it to seconds, you might do something silly and divide by 1,000, but here we have a more obvious solution: substr the last three digits off to create our $first_datetime and $second_datetime.

Using that, we can prepare a separate query for each day, looping across them to populate $customerCount.

Once we’ve collected all the counts in $customerCount, we then push that into $result4. And then we push the empty $result2 into $result4, followed by the equally empty $result3, at which point we can finally return $result4.

There’s no $result1, but it looks like $customerCount was a renamed version of that, just by the sequence of declarations. And then $min and $max are initialized but never used, and from that, it’s very easy to understand what happened here.

The original developer copied some sample code from a tutorial, but they didn’t understand it. They knew they had a goal, and they knew that their goal was similar to the tutorial, so they just blundered about changing things until they got the results they expected.

Nancy threw all this out and replaced it with a GROUP BY query.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

CodeSOD: Bunny Bunny

By Remy Porter

When you deploy any sort of complicated architecture, like microservices, you also end up needing to deploy some way to route messages between all the various bits and bobs in your application. You could build this yourself, but you’ll usually use an off-the-shelf product, like Kafka or RabbitMQ.

This is the world Tina lives in. They have a microservice-based architecture, glued together with a RabbitMQ server. The various microservices need to connect to the RabbitMQ, and thus, they need to be able to check if that connection were successful.

Now, as you can imagine, that would be a built-in library method for pretty much any MQ client library, but if people used the built-in methods for common tasks, we’d have far fewer articles to write.

Tina’s co-worker solved the “am I connected?” problem thus:

def are_we_connected_to_rabbitmq():
    our_node_ip = get_server_ip_address()
    consumer_connected = False
    response = requests.get("http://{0}:{1}@{2}:15672/api/queues/{3}/{4}".format(
        self.username,
        self.password,
        self.rabbitmq_host,
        self.vhost,
        self.queue))

    if response and response.status_code == 200:
        js_response = json.loads(response.content)
        consumer_details = js_response.get('consumer_details', [])
        for consumer in consumer_details:
            peer_host = consumer.get('channel_details', {}).get(
                'peer_host')
            if peer_host == our_node_ip:
                consumer_connected = True
                break

    return consumer_connected

To check if our queue consumer has successfully connected to the queue, we send an HTTP request to one of RabbitMQ’s restful endpoints to find a list of all of the connected consumers. Then we check to see if any of those consumers has our IP address. If one does, that must be us, so we must be connected!

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

CodeSOD: A Truly Painful Exchange

By Remy Porter

Java has a boolean type, and of course it also has a parseBoolean method, which works about how you'd expect. It's worth noting that a string "true" (ignoring capitalization) is the only thing which is considered true, and all other inputs are false. This does mean that you might not always get the results you want, depending on your inputs, so you might need to make your own boolean parser.

Adam H has received the gift of legacy code. In this case, the code was written circa 2002, and the process has been largely untouched since. An outside vendor uploads an Excel spreadsheet to an FTP site. And yes, it must be FTP, as the vendor's tool won't do anything more modern, and it must be Excel because how else do you ship tables of data between two businesses?

The Excel sheet has some columns which are filled with "TRUE" and "FALSE". This means their process needs to parse those values in as booleans. Or does it…

public class BooleanParseUtil { private static final String TRUE = "TRUE"; private static final String FALSE = "FALSE"; private BooleanParseUtil() { //private because class should not be instantiated } public static String parseBoolean(String paramString) { String result = null; if (paramString != null) { String s = paramString.toUpperCase().trim(); if (ParseUtilHelper.isPositive(s)) { result = TRUE; } else if (ParseUtilHelper.isNegative(s)) { result = FALSE; } } else { result = FALSE; } return result; } //snip }

Note the signature of parseBoolean: it takes a string and it returns a string. If we trace through the logic: a null input is false, a not-null input that isPositive is "TRUE", one that isNegative is "FALSE", and anything else returns null. I'm actually pretty sure that's a mistake, and is exactly the kind of thing that happens when you follow the "single return rule"- where each method has only one return statement. This likely is a source of heisenbugs and failed processing runs.

But wait a second, isPositive sure sounds like it means "greater than or equal to zero". But that can't be what it means, right? What are isPositive and isNegative actually doing?

public class ParseUtilHelper { private static final String TRUE = "TRUE"; private static final String FALSE = "FALSE"; private static final Set<String> positiveValues = new HashSet<>( Arrays.asList(TRUE, "YES", "ON", "OK", "ENABLED", "ACTIVE", "CHECKED", "REPORTING", "ON ALL", "ALLOW") ); private static final Set<String> negativeValues = new HashSet<>( Arrays.asList(FALSE, "NO", "OFF", "DISABLED", "INACTIVE", "UNCHECKED", "DO NOT DISPLAY", "NOT REPORTING", "N/A", "NONE", "SCHIMELPFENIG") ); private ParseUtilHelper() { //private constructor because class should not be instantiated } public static boolean isPositive(String v) { return positiveValues.contains(v); } public static boolean isNegative(String v) { return negativeValues.contains(v) || v.contains("DEFERRED"); } //snip }

For starters, we redefine constants that exist over in our BooleanParseUtil, which, I mean, maybe we could use different strings for TRUE and FALSE in this object, because that wouldn't be confusing at all.

But the real takeaway is that we have absolutely ALL of the boolean values. TRUE, YES, OK, DO NOT DISPLAY, and even SCHIMELPFENIG, the falsest of false values. That said, I can't help but think there's one missing.

In truth, this is exactly the sort of code that happens when you have a cross-organization data integration task with no schema. And while I'm sure the end users are quite happy to continue doing this in Excel, the only tool they care about using, there are many, many other possible ways to send that data around. I suppose we should just be happy that the process wasn't built using XML? I'm kidding, of course, even XML would be an improvement.

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

Our dog Camel

By noreply@blogger.com (Jon North)

Over 250 years the English poet Christopher Smart wrote the poem For I will consider my cat Jeffrey.  Now we think constantly 'for we will consider our dog Camel', the new friend at 74 rue de la Brechette following Evie's sad death last autumn.  Her replacement is several times her size!!


Last week we went to Figeac, 3-4 hours' drive west of Lunel, to collect our new companion, a 10-year-old male dog called Camel.  He had lived for 4 years in the refuge in the remote hilltop locality Nayrac a couple of km outside Figeac on the river Lot.  The refuge is operated (since the late 80s) by  MASAQ, the Mouvement Associatif pour la Sauvegarde des Animaux du Quercy, a very caring and committed group which seems to be struggling to cope with demand - the 100+ dogs have to be housed, fed and walked by volunteers and they are completely full up on the cat side, and the refuge has shelter and some limited facilities, but little warmth or protection for the many animals in the woodland setting surrounded by rubbish processing plants at the end of a long winding dead end road.

Camel is a placid, friendly animal, underweight and in need of more walks and more food, carefully controlled to allow for his rather shrunken stomach, and now just starting to get used to our warm dry house!  He was chipped and checked by their vet before we could collect him and sign the adoption papers, and given a thorough check by our own vet the day after he arrived.  But if he seems bemused to find himself in our home it isn't surprising - he'll still be getting used to being warm.

Figeac itself is a really nice little town with a really lovely medieval centre which we explored despite the annoying closure of the Tourist Office for staff training!  But going to the Mairie instead to get a town plan was a bonus, because the young woman at the desk turned out to be a volunteer at the refuge and of course knew one of their longest-staying residents!  She and we were very glad to say hello before we went to find him.

Here is an album of photos of Camel in his first few days with us.

Road trip

By noreply@blogger.com (Jon North)

Interrupting my enjoyable accounts of meetings with family and friends in England, a trawl around out 3500 km plus round trip to England from the S of France.

We decided to drive because we thought of taking French wine to our family.  In the process we kept some Burgundy for ourselves from our favourite Jacob vineyards - crémant, Chambolle Musigny and some white Hautes Côtes de Beaune matured in Acacia barrels which is less easily available so we were lucky to find it!  Sadly, on our return through France we could not find a chance to revisit Echevronne but we'll be back, no doubt.

Perhaps the highlights of the route and the roads we travelled were or will be the Millau viaduct which will more or less welcome us back into the Languedoc when we return home down the A75 tomorrow, and the quiet journey from north of London up the A5 towards Staffordshire as we started our visit in December.  the Narrow Boat pub on the Grand Union Canal has been an enjoyable stopping place over the years.  We returned south on the A5 but sadly the pub was not yet open for coffee as we passed.

But the most surprising pleasure is the moment on the M25 near Rickmansworth when it passes neatly under the Chalfont Viaduct - as the Atlasobscura website relates "Those impressive Edwardians thoughtfully left enough room between the arches of this grand 1906 railway viaduct for an eight-lane motorway to pass through".  I can't help thinking the motorway designers had to back off a considerable distance to line up the M25 through those excellent brick arches.

Our almost daily journeys from near Burton to Wirksworth, between the houses of our sons, gave us time to refine our route - the picturesque but windy one via Ashbourne was rapidly replaced by the smoother but still enjoyable route via the A38 and A50, quicker and less demanding!

We have learnt to pace ourselves, shorter stages with hotel stops, but still on these short winter days the end of each day is a challenge as streams of headlights approach us in the gathering darkenss as we attempt to decipher the directions to the next hotel.  And even welcome sunshine in France today presented problems as we negotiated ring roads and roundabouts.  But we enjoyed the sights we could spare time to see - Chartres cathedral against the skyline, and a sliver of crescent moon as we drove towards Orléans.

The car ferry is a gentle respite on the route, and the Channel Tunnel is so quickly over in a car that you scarcely remember it before you are on the road again.  But on short winter days perhaps the tunnel is a better bet, leaving more daylight hours to drive safely in.  Changing sides of the road no longer presents too many challenges - I always feel that the signage and road markings are clues enought to which side of the Channel and of the road you are currently on.  And we saw no gilets jaunes at all on the way up,though several stood by their roadside fires and did not bother us as we passed Dieppe, Evreux and Dreux on the way down.

An anniversary and other Christmas moments

By noreply@blogger.com (Jon North)

Our 40th wedding anniversary was in October, but since we could not meet the family then we arranged a meal together just after Christmas.  It was lovely to have all our sons and their families with us for an evening of Italian treats in Oakerthorpe, where the Peacock pub has been rebranded Pesto at the Peacock!


We shared our time over the festive period between the homes of Jeff & Fi and Sam and family, enjoying several exchanges of presents, walking around the Staffordshire countryside and taking occasional trips to shops in Burton and Ashbourne, as well as catching up on tv and sleep!

Jeff & Fi's cottage across the fields
 






Some Christmas moments at Marebrook

By noreply@blogger.com (Jon North)

More photos from our stay at Jeff & Fi's









Waiting for Christmas in Staffs and Derbys

By noreply@blogger.com (Jon North)

Our days leading up to Christmas this year have been spent with the usual mix of shopping, wrapping, watching telly and spending time with our family.  This is a little photo diary of our first few days in rural Staffordshire.

  




 




Christmas trip part 2 - to the Midlands

By noreply@blogger.com (Jon North)

NB photos of Staffs taken on an earlier winter visit
 I'm writing in the early morning of Saturday 22 Dec in a cosy cottage near Burton-on-Trent, home our son and daughter-in-law Jeff & Fi, and our temporary resting place in England.  We've driven nearly 1500 km to get here with amazingly little difficulty - not only avoiding hold-ups in France but making the journey from Kent to London and then on to Staffs smoothly on quiet roads.  All the more amazing on the brink of a holiday weekend when all reports are of heavy traffic and jams.  They all seem to have happened after we passed!

Our short stay in London with old friends Ruth & Claus was comfortable and warm in every sense.  We had a chance to wander in Highgate village and introduced two sets of friends from different parts of our lives over lunch - if there was a common thread beyond a general enjoyment of culture it was the appreciation of good wine: we'd brought with us wine Gaynor & Ed had bought in Bordeaux during our summer visit there together, and we enjoyed both good Bordeaux red and delicious Chablis with lunch.  I keep straying in this blog into the territory of the wine one!

Highgate scenes

So we set our yesterday from London, aiming for the A5.  This lust be one of the least diverted of the old Roman Roads in the country, once you get onto it north of St Albans.  Of course it has new bits like the Milton Keynes bypass that Romans would have found it hard to imagine (even without the concrete cows) but mostly it passes through delightful undulating rural landscapes all the way to Leicestershire where we turned off towards Burton.  On the way up we stopped for some lunch at a really good raodside pub, the Narrow Boat at Weedon (now we discovered with Motel-like rooms fringing the car park).  We visited this quite often on our trips to and from Derbyshire some years ago.  The grounds are right next to the Grand Union Canal and it's altogether a good place to visit.  We arrived at Jeff & Fi's at 3 pm and enjoyed a cosy evening with a blazing stove, good wine(!) including some excellent Aldi organic prosecco we'd discovered on our last visit and some beautiful pinot noir we'd brought from the Limoux vineyard of Jean-Louis Denois.  A good start to our holiday.

Christmas in England - arrival

By noreply@blogger.com (Jon North)

With the uncertainties of Brexit on the horizon, this seems like a more than usually important time to visit our family in England, and we are really looking forward to being with Jeff & Fi, Sam, Sas, Heather and Ben and also seeing Ed, Isla and Karen for a while.  Lots to look forward as I sit in our Folkestone hotel which was a nightmare to find in the wet and dark, but it all felt better after a drink in the bar!

The past two days started early, very early, as we drove up a dark and almost deserted A9 from Lunel to our overnight stop in Burgundy, not quite knowing if the gilets jaunes would add to the uncertainty of Brexit.  Weekend reports were not encouraging - we read of fires on and by motorways and toll booths, a whole section of the A7 from Avignon to Valence closed, numerous entrances and exits closed as our own local ones had been on and off for the past few weeks.  We started out full of anxiety, just hoping that after the weekend the demonstrators would all heed to colder damper weather and go home.  

All sorts of thoughts have crowded in on us as we've driven 1,000+ km up France.  We drove without seeing a single gilet jaune. Tant mieux, and of course there are fewer in the north than in the south, fewer in midweek than at weekends, fewer now than there were 2 months ago. But we were reminded that until recently a frequent electronic message on motorways here used to be 'respect the men in yellow’, and today it says something like ‘agents anticipés’. Does not trip off the tongue as the other used to, but no danger of mistaking workmen for protestors, at least in the wording of messages!  

One final thought about these demonstrations - we read in a local paper in Burgundy that local groups of gilets jaunes are hoping to form associations so that they can be eligible for grants from public authorities!!  So people who began by demonstrating against too high taxes are now hoping some taxation will be spent on their groups.  In fact, merssages are mixed - some want lower taxes; others want to preserve services which are being cut.  On top of that, some have destroyed lots of things, motorway toll booths and sometimes the roads themselves with fire.  Vinci (one of the motorway companies) reckons they will need to spend millions repairing things.  Not easy to square with keeping up tax-funded services.  But enough, we don't understand everything and no doubt there are important causes to be fought.  The rest of this will be, more pleasantly, concerned with our doings.

Our first port of call in Burgundy was our friends Jean-Michel and Christine Jacob, whose wine will accompany us to England and (some of it) back home.  It's always a pleasure to see them again, but their story really belongs in my wine blog where I've often written of them (here for example), so suffice it to say that we were warmly received and delighted with the wines we found and could take away, as we hope our family and friends will be!  We had a good simple lunch in Beaune, a pleasure to revisit this lovely town, and then sought our our hotel.


lunch in Beaune - Mary appears twice thanks to the mirror next to our table!
The hill of Corton from our hotel terrace
We'd chosen a Logis hotel in Ladoix Serrigny, on the south-eastern flank of the famed Hill of Corton (origin of grand crû wines which we did not seek out this time, both red and, unusually north of Beaune, white (Corton Charemagne).  It was a simple but comfy overnight stop and we set out at leisure for the north today.  The roads were quiet, the weather mostly dry and often bright, and the middle stretch in particular as we approached Troyes across the Fôret d'Orient was full of the amazing variations of colour and texture of winter scenery, branches, fields and hedgerows.  The last stretch past Reims and on up to the coast is long and less interesting to look at, but we made the Tunnel hours ahead of the time we'd planned and so crossed into wet Kent and our first night on English soil, looking forward to seeing people as we travel north tomorrow and after.

September wine fairs

By Jon North (noreply@blogger.com)

The French 'rentrée' is also the start of an interesting period for wine enthusiasts - the season of wine fairs in supermarkets.  In all, these span nearly 6 weeks, and these days the wines are not only good value but also carefully selected.  As with medal competitions, you always have to bear in mind that makers who already have a good market for their wines need not participate, but with the aid of review articles you can usually buy good wines at good prices.  Since the best bargains are usually snapped up early, the secret is to arrive at the supermarket at opening time on day 1. 

I did this several times in September, partly for our wine tasting circle here in Lunel and partly to find wines as presents.  The wines I bought are listed below, but first a few notes on the different places I visited and on some of my choices.  I was aided by the comprehensive article in the Revue in August, which highlighted a dozen or so top picks from each chain.

The earliest wine fairs are in the cut-price supermarkets - Lidl, Aldi, Netto and Leader Price.  One could add BioCoop but their wine fair sas so chaotic that they could not even agree on a start date, so lost out as I arrived early on the first day of each.  That’s what you have to do to snap up the best bargains.  Lidl is the most impressive, with three aisles dedicated to a huge range of French and a few foreign wines.  As you can see, I found plenty of choice even without the usual array of Bordeaux reds.  Rhône reds and a nice Touraine white were my picks here.

For the rest, the budget stores ranged from the chaotic Netto (lucky to find any of the wines listed beforehand) to the interesting but slightly disorganised Leader Price and the very nicely organised Aldi, whose range I’ll explore more next year.  Although the major supermarkets’ fairs start later in September, or into October, I managed to find one of my star buys in Intermarché and (as I have done often in previous years) some good buys in Leclerc.

Two personal stories link to my wine fair visits this year.  The first is a red from the flat lands between Orange and the Dentelles de Montmirail in the southern Rhône, from the village of Violès.  When we first discovered this it followed a purchase from the organic shop Beanos in Matlock Bath, which we used a lot during our time in Derbyshire.  At that time Violès was on the bottome rung of the Côtes du Rhône, a sleepy village en route to more celebrated places like Vacqueyras, Gigondas and Beaumes de Venise.  Since then the village has acquired Côtes du Rhône Villages status itself, and the Tour des Abbesses we found in Inter was one of the best reds we’ve come across recently for  everyday drinking.  I seek it out when I visit Intermarché stores in the Cevennes, but I doubt it will be sin stock for much longer.

Second, one of the highlights of my visit to Aldi was the Bonnezeaux from a well-known producer, Château de Fesles.  The sweet chenin blanc wines from this area (the Layon valley south of Angers) are exceptional and long-lasting, one of the few white appellations outside exalted Burgundies to keep more than a few years.  But is is for this very reason that makers divest themselves of wines 15 years old or more.  We first discovered this in the 1990s when we bought 1979 Bonnezeaux from the very same producer from 3D wines, who introduced us to some of our favourite makers elsewhere.  Because these wines last so long, makers hold onto stocks but in the end have to sell them to make room in their cellars, and we profit.  This Bonnezeaux was not a great wine, but a delicious wine to drink now all the same, and it will keep a few years yet.

By the way, a late purchase not on my list below is a Savennières, another Anjou wine but this time dry but equally longlasting.  Sadly the bottle we bought from Leclerc will not be tested for longevity because we finished it between us at lunchtime today, but there is another lurking and I’ll be getting back to Leclerc in the vain hope that there may still be some left - utterly delicious.

Here is my list of purchases, 7 whites first, the rest red.


Saint Véran Louis Dailly 2017 Leader Price 8.04 €
Macon Villages Cave d'Azé 2017 Netto 4.99 €
Touraine Sauvignon Caves Gilles Gobin 2017 Lidl 3.99 €
Menetou Salon Patient Cottat 2017 Intermarché 9.95 €
Sancerre Les Fossiles, dom Roblin 2017 Intermarché 13.75 €
Alsace Riesling Rittimann Celliers de Romarin 2016 Aldi 5.99 €
Bonnezeaux Château de Fesles (50 cl) 2001 Aldi 13.99 €
CdR Villages Dom de Tavans 2017 Leader Price 5.87 €
Cahors Malbec du Clos 2015 Leader Price 5.33 €
J L Baldès
Saint Joseph Dom de Blacieux 2017 Lidl 9.99 €
Vacqueyras Terroir des Dentelles 2016 Lidl 6.99 €
SCA Rhonéa, Beaumes de Venise
Juliénas Collin-Bourisset 2017 Lidl 5.99 €
Vinsobres Dom Croze-Brunet 2016 Lidl 5.49 €
Côtes du Rhône Vill. Dom la Tour des Abbesses, Plan de Dieu 2017 Intermarché 4.49 €
Gaillac Gd réserve de Labastide de Lévis 2016 Aldi 3.99 €

Visiting the Val du Séran

By Jon North (noreply@blogger.com)

We come to the Ain almost every year to play and sing music at Val du Séran, a huge converted farm building in the lovely countryside between the Bugey area and the upper Rhône.  It's quite isolated, surrounded by green hills with some of the cows that produce milk for Comté cheese, and with more distant views of the pre-Alpine mountains.  But apart from the stimulating guidance of our host Stéphane and the marvellous cooking of his wife Chantal, we always enjoy the wines they choose to accompany our evening meals, usually showing a sure touch  in matching food and wine.

This year, for example we've had a Bordeaux Clairet accompanying tarragon chicken.  We came across this on our trip to Bordeaux earlier this year - a light-coloured red wine (like a dark rosé) usually made from merlot grapes.  Then, a Californian cabernet sauvignon full of sweet vanilla fruit in a style which is definitely not French, a really good match with a scented but mild beef curry; a smooth Fitou (from the area south of Narbonne, not far from us) with a veal and ginger dish; a beautiful red Côte du Rhône  with barbecued rissoles made from a Croatian recipe; and a Fleurie (one of the Beaujolais crûs, usually regarded as relatively light and floral) with a pork mignon served with a haricot purée.  A couple more nights of this before we return to our own less diverse choices at home.

When we come to this area, we also enjoy the local wines, particularly the white Roussette de Savoie and Seyssel, made from local grape varieties Altesse and Molette.  These are lively flavourful wines which work well as apéritifs, as Mary proves most evenings after a full day of cello playing.  AT Stéphane's recommendation we visit the maker Bernard Aimé in the village of Corbonod near the town of Seyssel itelf and close to the river Rhône which is very picturesque in this area.

We have good lunches here of salads, cheese and fruit, but (unlike our home habit) no alcohol at lunchtime so that the music is not marred by sleepiness or lack of attention!  But the evenings are a time to relax and feast, as you can see!

Living with pain

By noreply@blogger.com (Jon North)

The past year for me has seen a slow ratcheting up of pain from sciatica.  Before that I would not have believed its all-consuming force.  Even so, I am well aware that I am lucky not to have worse, and the exercise of trying to stay positive is both constructive and interesting.  When we arrived in France Mary spent months with severe tendinitis in a shoulder which she would otherwise have used to bow her cello - upsetting and frustrating but ultimately thank goodness cured.  Since then we have both had joint pain, and sod's law says that at our age this is usually in a place linked to your most creative and important activity.

Treating pain is a complex obstacle course full of blind alleys and treatments which have worked for other people, enthusiastically advocated, but they seem to have no effect when you try them yourself.  Ultimately painkillers are OK, but they kill more than the pain you are trying to treat, and if they do not upset the digestive system or attack the stomach lining they are more or less addictive.  I take them anyway, and they work at least for periods of time.  'Alternative' treatments work if you believe in them, and I tend not to which makes me a poor patient!

With the sciatica I have also been through the gamut of steroid treatments which are very good for a day or three, then just stop working, back to agonising mornings.  Surgery is something I've had with one knee replacement, with mixed results though I can walk - it is like having a posh new hinge on a door, but someone forgot to oil it properly.  Since sciatica involves eroded discs in the spine it is another level of complexity and risk altogether, and I'll go some distance to avoid it despite our appreciation of the talents of Supervet Noel Fitzpatrick.  The animals he treats are after all much less heavy than I am!

But exercise and physical treatment are another matter.  I enjoy cycling, and riding my bike around our lovely flat town or using my exercise bike at home are both constant parts of my life.  The pain goes down, and either I can read and listen to music, or enjoy passing scenery, whhile I finish a session or ride with less.

I've just got back from my latest session with a physiotherapist.  I have had several of these over the past 5 years or so, before and after knee surgery, and my expectations were low.  I'm not the only person, patient or doctor, who recognises the description of being left hitched to a machine (pulley, electrodes, whatever) for 20 minutes while your therapist attends to 2 or 3 other clients.  An hour in a treatment room with only 10 minutes' direct treatment is not uncommon.

I'm not writing to moan about bad examples of physiotherapists because this time I think I have finally found a good one.  A young man who spent 29 mins of the allotted half hour with me, pushed and pulled me both to check my limits and push them a bit, and most importantly left me with exercises I should be doing several times a day at home.  To be fair to the last man he proposed something similar, but not very well explained.  I know now what stretching I need to do and how often, and I'll do it even though my rubber mat on the floor is less convenient and comfortable than his therapy couch.

I'll see him regularly over the next 6 weeks or so.  But in the end the answer is going to be in my own hands, or legs maybe - profiting from my enjoyment of cycling outdoors and in, putting together the advice of my GP, the rheumatologist, an osteopath who tweaked my vertebrae once, and my new physiotherapist that the best treatment is going to be more exercise.  And overcoming my innate laziness to get down on the floor and do the spinal stretches every day.

Incidentally, I was puzzled about the link between the term rheumatology, all things connected with rheumatism, arthritish and pains generally in joints and muscles, and the original French/Latin root rhume meaning cold or sniffles.  Apparently ancient medicine regarded these painful conditions as linked to watery humours - who knew?  The Oxford Dictionary certainly did and does!

At the same time I remember all the time that other people (family, friends and those I meet around the place) have more serious difficulties.  I was reminded of this again this morning seeing others arriving at the physio centre as I left to ride my bike home in the sunshine.

Reading

By noreply@blogger.com (Jon North)

Well here I am again, the inconstant blogger.  I have more time to write just now, and more time to read, so this will be about reading, and specifically in praise of the Kindle.  I've nothing against other brands but I'm not the BBC so need not display impartiality and I have little experience of other electronic readers.

I am a librarian and booklover, so of course I understand and share many people's feelings that Real Books are still of value.  Some would say they are far better - Mary usually reads them, and I often do, but often I turn to my Kindle and here's why.

First of all, I read in bed.  A heavy book can be difficult to manage, and if like me you often read when your partner is asleep, a Kindle is light enough to hold in one hand and its inbuilt light is a boon.

Secondly, and I appreciate this as a librarian, you can search text.  As I get older I often lose track of the names of people and places, and find it helpful to check back to the first or a previous reference.  Who was that?  When did the place crop up or what did the person do?  On a Kindle I can check quickly back and resume reading.

Thirdly, in my frequent need to read in a foreign language (my French is improving, but slowly) if I come across a word I don't remember or have never seen before I can quickly check its meaning through the built-in dictionary.  This is a real boon, and there is a way of noting newly discovered words in a vocabulary builder which, after a while, is a good read on its own!

Fourthly I get my daily paper first thing in the morning.  Paper copies are among the most famously cluttering things in many households I have known, often piled high in garages and spare rooms when their owners cannot bear to throw them out 'in case', but they are scarcely ever re-read after a day or two.  The online papers are not there for ever but for a couple of weeks you can search, then clip and store things that interest you.

People who cling fiercely to books and never read electronic text talk of the feel and form of a real book.  I share some of this feeling, especially having worked with older out of print books, but I realise that these become fragile and should finally be conserved and consulted via scanned copies or they will be damaged beyond repair.

I don't think I shall ever stop liking having books around, but they take space and collect dust - logically a Kindle is more environmentally friendly option, and it's amazing to think that there can be scores, hundreds, of books available in one small device, more easily stored and downloaded at will.  And as travelling with heavy books is ever harder, a single device with months of good reading in it is a welcome asset.

Thinking back to our holiday in the Caucasus

By noreply@blogger.com (Jon North)


I have been wondering, thinking more or less non-stop, and dreaming as well, how to round up our extraordinary fortnight beyond the Black Sea and on the boundary between Europe and Asia.  We went knowing so little, and we have found out so much about these extraordinary small countries in the mountainous triangle between the Russian, Turkish and Iranian great powers.

We visited Armenia and Georgia, for only a week each, and could hardly have contemplated visiting Azerbaijan which is the third of the trio because its relations with Armenia are so poor.  Indeed, on a trip north and wesst around Armenia we were advised to avoid the road near the western border in case of snipers.  Since our return we have reflected as much on the shared history of the countries we visited as on the differences which struck us most forcibly during our visit.

It was only as we started to read good guidebooks (we'd especially recommend the Bradt Guide to Armenia by Deirdre Holding, and the Georgia companion by Tim Burford (Bradt also) is not bad either), and then discovered a wonderful history and contemporary survey The Caucasus: an introduction by Thomas de Waal, that we began to understand the interwoven complexities of this fascinating area.

The Genocide Memorial in Yerevan
Armenia, our first port of call and now a landlocked country of only 3+ million people, was perhaps historically the most significant, having for centuries stretched from the eastern Mediterranean through Turkey across almost the whole of the area now divided between Armenia, Georgia and Azerbaijan.  Its current identity is marked by loss - loss of territory to the Turks (who now claim the sacred Mount Ararat once the centre of the Armenian highlands from which the ethnic group originates), loss of people to the awful genocide of the early 20th century, and loss of stability in the face of war (still smouldering with Azerbaijan over the disputed territory of Nagorny Karabach) and of natural disaster in the shape of earthquakes, since the area is on a geological fault line.   One town, Spitak, the size of Lunel was almost destroyed in 1988 and most of its inhabitants killed.  The damage was still all too evident, with temporary homes in containers and railway carriages, as we travelled through the area.

Our second week in Georgia was primarily to meet again our friends Leo and Marika, whom we'd hosted in Wirksworth in the early 2000s.  We added organised tours to fill our time during the week when they had to work.  We had a quaint but comfortable hotel in the old town of Tblisi, whence we could walk around the centre and visit museums and churches, and even attend a marionette show in the charming puppet theatre (in Georgian but with English surtitles, a very entertaining folk tale that ended up with the principal characters in Paris!).
Pirosmani's Doctor on a donkey

Karen Hakobyan's instruments
Highlights of our visit included artistic visits to the beautiful small museum in Yerevan dedicated to the well-known film director Sergei Parjanov - we loved it and went back to take in more detail before leaving Armenia - and the beautiful small gallery of the artist Niko Pirosmani in Signagi.  We also met Karen Hakobyan, a maker of traditional musical instruments including the duduk, in Yerevan - a real inspiration, and I have an alto shvi, a traditional fipple flute, as a lasting memento.  The instruments are made of stained apricot wood, the traditional material.

The separate identities of the two countries were really only crystallised under Tsarist and particularly Soviet influence, one effect of which was to precipitate huge population shifts to create 3 ethnically distinct countries (it would have been diffficult if not impossible to visit Azerbaijan even if we had had time).  I read that at one time in the 19th century almost no ethnic Armenians lived in present-day Armenia, huge numbers residing in what is now Iran and many also living in Georgia (our Armenian driver Ashot, for example, was brought up in Tblisi).

But communist rule (and particularly the longs shadows of Stalin and Beria) served to sow distrust between ethnic groups which separated like oil and water.  Armenia now has a population 98% ethnically Armenian, and although Tblisi has a more cosmopolitan feel (with a mosque where Shia and Sunni worship together and Christian churches of all the various traditions), there is a feeling of bravado and of Georgian national pride as strong in its way as the Armenian one.

The distrust, and the story of the south Caucasus, revolves round ethnic and cultural minorities, not only in Nagorny Kharabakh where de facto Armenian control has left a seemingly permanent impasse between the opposed positions of Azerbaijan and Armenia that have resisted repeated international conferences all over the world and which makes any reconciliation between Armenia and Turkey even more difficult than the genocide left it, but in the north of Georgia where two autonomous regions, Abkhazia and South Ossetia,, have been set up with Russian support.  The history of these two areas is long and complicated, but it has left most Georgians with a deep distrust of Russia and a strong preference for territorial integrity.  This in turn means they support Azerbaijan in claiming back Nagorny Kharabakh which sets Georgia at odds with Armenia, although luckily the antipathy is not extgreme so you can still travel as we did from one to the other, and Armenian commerce has access to the Black sea through the southern province of Georgia where there is still a high Armenian population that has resisted ethnic cleansing.

Traditional bread-making in Garni, Armenia
Two final things for now.  First food and drink.  We had excellent food throughout, with lots of fresh fruits, vegetables and salads particularly in Armenia and some lovely soups in Georgia as well as the ubiquitous and stomach-lining khachapuri, variations on warm flat bread filled with cheese, one delicious variant of which has an egg lightly poached on top.  The Armenian wine industry is less developed than the Georgian but both use indigenous grape varieities to good effect, particulalry the red Areni (centred round a village of that name in the south) in Armenia and some good dry whites in Georgia.  But Georgia also prides itself on good sweet red and white wines made from late-picked grapes.  And then there is Armenian brandy, reputedly Churchill's favourite tipple, and the ubiquitous vodka which testifies to the strength of the Soviet influence.

Drink at the Botanic Gardens, Tblisi
Roadside food stall in Tblisi old town
Wine tasting at Château Mukhrani west of Tblisi
Second, Stalin.  An odd note to finish on, maybe, and our Georgian friends doubted if we really needed to visit the museum in Gori which he himself established.  The little town still retains his statue in hte centre, and his childhoold house is preserved under a kind of huge bus shelter.  He adopted the last Tsar's railway carriage to get around, more or less without modification.  But we were helped by a suitably irreverent English-speaking guide, himself a native of Gori.  Above all this was a reminder (in a world where Brexit and Trump remind us how near the surface divisive extremism can be) that the fragile free countries we visited are there partly as a result of Stalin who, for better or worse, permitted the development of separate states with separate identities, and partly despite the cruel bloodshed that has surrounded them throughout their history and virtually until the present.

View over Tblisi to the mountains north-west of the city
Perhaps the most powerful influences that have shaped them and allowed them to survive and develop, though, are the mountains - the High Caucasus to the north which so hampered easy Russian access to the southern countries, the mountains between Turkey and Armenia which while sacred to the latter are also a brake on easy passage, and the mountains within the countries which have allowed separatist enclaves to survive.  It also makes for lovely scenery for us tourists, even if there is a risk of the early snow we ran into twice in our fortnight.

The larger Mount Ararat with Khor Virap monastery in the foreground
Mount Ararat from the 7th century circular cathedral of Zvartnots south of Yerevan


A week in Georgia

By noreply@blogger.com (Jon North)


 I had hoped to write this sooner, but getting back home I had to catch up on life in France, so this has been the first chance I've had to publish this post about our second week away.

It was of course the original reason for our Caucasus trip - to visit the couple of Georgian friends Leo and Marika whom we had hosted while Leo was a postgraduate architecture student in Nottingham.  But they both have to work, so having spent a weekend with them we joined up with the guide Eva (also seen here) and driver Ilya for a few days' tours to places outside the capital.  The weather turned wet and cold until our last day, which spoiled a lot of views, but we still saw a lot.

Our hotel in the Old Town was comfortable and convenient for walking around the tourist centre of Tblisi, which we did often during our week's stay.  The travel agent had arranged daytime trips during the week, but over the weekend Leo drove us east to an area, Khakheti, which produces most of the country's wine.  We visited monasteries and a town wine festival, and also a gallery including works by Pirosmani, one of the country's most popular modern artists who liked his wine as you can see).

                                   

                          

We spent the week, while Marika and Leo were at work, exploring the area west of Tblisi, including the old capital of Georgia and its cathedral, a monatery overlooking a lake and a snowy mountain pass, then later the château Mukhari, a winery of some quality.

Probably the outstanding visit of the week was to Mtskheta, the old capital of Georgia, which is just west of Tblisi at the confluence of 2 rivers.  The 6th century Jvari monastery is on a promontory overlooking the rivers, and the 11th century cathedral of Mtskheta just below.  Both were impressive - photos to finish this brief post, and more to come as I reflect on the complex, intertwined history and culture of the two countries we visited over this memorable fortnight.


 




















A week in Armenia 14-20 October 2016

By noreply@blogger.com (Jon North)

We'd thought of visiting our friends in Georgia for a few years, and Mary finally made the arrangements this summer as we celebrate 40 years together.  She found a travel agent in Montpellier which does Asian tours including an Armenian trip with an extension into Georgia, and since the season is nearly over they offered us an individual fortnight's holiday for 2.  This week we were in Armenia, and next we'll be in Georgia whence we fly home to France.

So this is a bit about our first week.  The best story is told through photos, but although I can easily put these on Facebook from this tablet, getting them into a blog will be much easier once I'm home with my laptop.  For now, some verbal impressions.  We travelled to Yerevan via Vienna, arriving in the early hours of Friday last.  Our guide Marietta and driver Ashot met us at the airport and have accompanied daily with friendly and professional care - they usually leave us evenings to recover, but this evening they've invited us for a farewell meal before they both accompany us to the airport at 5 tomorrow morning.

We have spent most of the time based in Yerevan, a sprawling mixture of Soviet blocks, shacks, building sites and splendid modern plazas and developments, home to around a million people, with very busy roads, and few bikes or motor bikes.  Roads outside the capital, and even back roads in Yerevan, are mostly in a poor state, and Ashot has needed all his skill to keep us out of most of the potholes in his Mercedes minibus.

Marietta, who is proud of her French, is even prouder of her country and people.  In fact, many people we met were anxious to get our opinion and approval of Armenia.  We had little real knowledge of it before we came, and only found a really good guidebook (in the Bradt guides series - http://www.bradtguides.com/destinations/europe/armenia.html) after our arrival, but we rapidly realised that this small nation with its quirky and unique alphabet derives its intense national pride from its history, up to the present day, of defining itself in the melting pot of the Caucasus, between the powerful political jaws of Russia and Turkey and as a Christian country under pressure from Muslim neighbours.

We have visited Christian heritage in many monasteries and saw incredible and ancient remains including countless lovely carvings, and we witnessed briefly the Sunday mass and beautiful singing at the cathedral.  We discovered local traditions - musical instruments like the doudouk, bread making - and extraordinary artists like Sergei Paradjanov.  We have been to wine and brandy tastings and learnt about local grape varieties.  And we have eaten good food, based hugely around fresh fruit and veg and salads and cooked vegetables prepared in countless ways, interesting local cheeses, creams and yoghurts, plus nice meat especially in stuffings for vine leaves.  And drunk a lot of good coffee.

We have ventured out of Yerevan to do all this on day excursions, mostly on day excursions but once on a 2-day trip via the huge lake Sevan north near the Azeri and Georgian borders, a journey plagued by rain then snow which forced us to alter our route, all of which Ashot handled well despite dropping temperatures.  On that trip we were reminded of the latest catastrophic earthquake in the north-west - Armenia lies on a tectonic fault-line.  Tragedies near the epicentre were heightened by poor Soviet-era building standards so that whole buildings collapsed killing thousands.

Such tragedies, and wars, pale into insignificance beside the 1915 genocide which has occupied our minds increasingly towards the end of our visit.  Wikipedia and the official genocide memorial website give much valuable information but nothing quite prepares you for the terrible graphic reality of the museum exhibition witnessing to the enforced death of over a million Armenians expelled from their homes and neighbourhoods.

I could go on but it will suffice to finish with our own testimony to a friendly and hospitable people despite, perhaps partly because of the tragedies and economic hardship they have suffered and despite the difficulties many still face.  We are happy to do our small part to make this place and these people better-known.

High summer in Lunel

By noreply@blogger.com (Jon North)

Blog posts often seem to follow an important event or memorable outing, but it seems good now and then to mark humdrum routine and the lazy, hazy days of summer.  All around we see evidence of exciting holidays being taken by family and friends, and of course they have to go in school holidays, but we can now avoid the expensive, busy times and stay home when the roads are clogged.

We came back from the UK in late July to a still boiling hot Lunel - afternoons up to 40°. The evenings are now shorter, which is a pity, but it's also getting a bit cooler - one night the temperature dropped to 16, and when I walked to the doctor's early it was lovely and cool with high wispy cloud.  Lunel is always very dry, though it occasionally rains heavily - a storm one afternoon produced 4 mm of rain but lasted just half an hour.

We try to get the tasks done in the morning, sometimes eat on the terrace, and read, sleep or follow sport in the heat of the day.  The Olympics have plenty to interest us just now, and the radio cricket commentary is always good value with highlights on Channel 5 - we are at the limit for UK tv reception but we still see a lot luckily.  Our French tv has gone completely on the blink which is no big loss but our satellite man is coming to fix it when he gets back from his holiday at the end of the month.

Once the cooler evening comes we can take Evie for a walk and we often go to the canal path - we're lucky to have it so near.  Sleeping at night has been a problem in the very hot weather, so we are glad the nights are cooling a little.  Otherwise one of the coolest places is the car with air conditioning running, so we never mind going out to play music or for the odd trip to Montpellier or our French group on Tuesdays.  One night stayed up late to look out for shooting stars - we saw a few though we were a bit early.

We always enjoy good food and drink of course, and from time to time we go out to lunch at La Terrasse, our favourite town centre restaurant and bar.  The other day we both had lobster (thermidor) for the first time in our lives - delicious!





Why is the NHS listening to the siren voices of the vape manufacturers? | Victoria Coren Mitchell

By Victoria Coren Mitchell

The calls to make e-cigarettes free on prescription are outrageous when an actual cure for smoking is available

Public Health England has called for e-cigarettes to be made available on the NHS. This makes me so angry that I want to have a fag, although the last one I had was on 27 September 2014.

Ah well. I’ll settle for eating another packet of Minstrels and typing furiously.

Continue reading...

The checkout free shop is a wonderful idea, a machine will never judge you | Victoria Coren Mitchell

By Victoria Coren Mitchell

When we no longer have to impress the cashier – or fellow shoppers – we can buy what we like

Are you frightened of the rising machines? I try not to be. Machines are the future and being horrified by the future is so terribly ageing. Banging on about the misery of automated switchboards, the insecurity of online banking or the impersonality of email puts 20 years on you immediately, like racism or natural light. I try to avoid such things.

So, for me, it’s all “Good news, my local post office has shut down!”, “Ooh, you need a ‘registered account’ to buy cinema tickets, I couldn’t be happier!” and “Hurray! A leaked NHS England report says 111 calls will soon be diverted to a ‘diagnosis app’ instead of a person!”

Continue reading...

I did lose a pair of trousers once but they weren’t worth £1m, Boris | Victoria Coren Mitchell

By Victoria Coren Mitchell

The bankrupt tennis star’s admission that he has mislaid his trophies resonates with all of us who know we put that thing somewhere…

A s I sat with Boris Becker in the Riviera sunshine, each of us clutching a cigarette in one hand and a doughnut in the other, I thought: “This is my kind of sportsman.”

I didn’t know Boris Becker very well but I liked him enormously. Clearly, we both enjoyed the taste of a sugary butt on a spring afternoon. Also, we both loved a hand of cards; we were in Monte Carlo for a €10,000 poker tournament. I always found him approachable, friendly, unpretentious and nice to be around.

Continue reading...

Comparing unlike with unlike – it’s Whitehall’s secret new parlour game | Victoria Coren Mitchell

By Victoria Coren Mitchell

If the culture minister says that ambassadors should be paid more than BBC editors, we should next ask him how to choose between Victoria Beckham and a leopard

Well done Peston On Sunday, last week, for having three interview guests and triggering major news stories with each of them. That’s an amazing hit rate.

First guest Jeremy Corbyn hit the headlines for suggesting that the UK doesn’t really have a special relationship with America. Third guest Miriam Margolyes said “fuck” live on air. And the middle guest, shiny new culture secretary Matt Hancock, said that editors at the BBC should not be paid more than ambassadors.

Continue reading...

Molly tries to shuffle the pack | Victoria Coren Mitchell

By Victoria Coren Mitchell

The more things change the more they stay the same – even in a film about a strong woman

Can Hollywood fix itself? Is that already happening? Let’s go to the cinema together and find out.

It’ll have to be my local Everyman – a genteel chain where they transmit a lot of productions live from the National Theatre and sell yoghurt-coated nuts instead of Minstrels. Might not be your cup of tea. On the plus side, you can also get a cup of tea. It has to be that venue, because the trip has already happened.

Continue reading...

My Christmas present to you

By Victoria Coren Mitchell

Hate gift guides? Here’s an alternative to all those that insult you with dull ideas

The gift guides are out again! Regular readers will know how infuriated I am by those Christmas gift guides. Pages and pages of newsprint, all given over to the advice that we should consider buying our relatives a pair of socks or a bottle of wine.

Adding insult to insult, they invariably divide these “ideas” into relatives (“A lipstick for your wife! A book for your mum! A bottle of wine for Grandpa!”) just to make sure that nobody shops beyond the boundaries of age and gender stereotype. Not only must we buy the same old stuff every year, we must make the same old assumptions. Women love clothes. Kids love sweets. Men love golf calendars.

Continue reading...

Ed Sheeran versus the super-idiots | Victoria Coren Mitchell

By Victoria Coren Mitchell

Attacking the singer’s charity efforts takes a rare type of stupidity. But guess what...

According to a Dropbox survey published last week, most people believe that “only 68% of their work colleagues” are capable of the job.

This is a staggering figure. Why so high? Nobody’s capable of the job. Nobody’s capable of anything.

Related: Ed Sheeran Comic Relief film branded 'poverty porn' by aid watchdog

Continue reading...

Michael Gove, where did our love go? | Victoria Coren Mitchell

By Victoria Coren Mitchell

The environment secretary has shown a flash of his old character but I can’t forgive his betrayal

Did you wake up on Thursday morning expecting to feel a wave of affection for Michael Gove? I didn’t. I’m not sure anyone did. Possibly not even Mrs Gove, who wrote a very interesting joke in her Daily Mail column the day before.

“Kevin Spacey and Harvey Weinstein are reportedly languishing in rehab at an upmarket sex addiction clinic in Arizona,” it began. “No sex for eight weeks, apparently – although quite why they needed to fly to the desert for that is beyond me. Most of us find getting married does the trick.”

Continue reading...

Why are the police copping flak? | Victoria Coren Mitchell

By Victoria Coren Mitchell

Don’t damn officers for a Halloween prank – law and order should show it has bit of heart

It is possible that I have many things in common with Fenland police. You can’t do Only Connect for as long as I have without knowing that one thing can always be linked to another. Four things can be a challenge, but a simple pair, such as Fenland police and me… off the top of my head: we both like to wear blue, we’ve both dealt with a lot of drunks and neither of us has managed to stamp out hare-coursing in the county of Cambridgeshire.

And here’s another: we both tweeted pictures of ourselves dressed up for Halloween.

Continue reading...

Casinos gamble on their credibility | Victoria Coren Mitchell

By Victoria Coren Mitchell

If you’re too smart for gaming houses they’ll find a way to stop you - but more fool them

It is rare to see Phil Ivey, the greatest poker player of our time, losing seriously. This man is a genius. He can get inside other people’s heads.

The first time I played poker against him, I think he found me a little unsettling. People do, the first time. In Phil’s case, I don’t think it’s just that I was female – which is what throws most people – but that I was female and making jokes.

Continue reading...

Faster, higher, twerkier? | Victoria Coren Mitchell

By Victoria Coren Mitchell

Why stop at pole dancing at the Olympics? Let’s have groping and mud wrestling as well

The news that pole dancing has been formally recognised as a sport – and will now be considered for possible inclusion in the Olympics – fills me with delight.

Regular readers may be surprised. You might imagine I would feel weary and suspicious at this development. You might imagine I’d roll my eyes and ask: “What next? A simultaneous men’s event – how many bills can you shove in her bra as she writhes?”

Continue reading...

I’m really not a petrolhead... | Victoria Coren Mitchell

By Victoria Coren Mitchell

... but the thought of driverless cars and the surrender of freedom fill me with gloom

God bless the women of Saudi Arabia and their excitement about a royal decree allowing them to hold driving licences at last.

As we sit in traffic jams, fuming about inexplicable delays and unending roadworks, terrible radio playlists, the utter monotony and hell of it all, we should think of our sisters in the desert who see only the freedom, power and joy.

Continue reading...

Putting Granny online? No thanks| Victoria Coren Mitchell

By Victoria Coren Mitchell

I’m all for doing something for old people. But don’t ask me to put family snaps on the web

Today is National Grandparents’ Day. AgeUK is asking people to tweet, Instagram, hashtag (and other words your granny would not have understood) a photograph of their beloved ancestors, marked #grandpics, with a suggested donation of £5 with every photograph.

They asked me directly to do this. I said no. F*** em, I said.

Continue reading...

Oh, do let’s be beastly to the Nazis | Victoria Coren Mitchell

By Victoria Coren Mitchell

Nobody thinks Paul Hollywood is an actual Nazi; we should let him dress how he likes

A Nazi goes into a pub.

Hang on… that’s not a Nazi! It’s the well-known baker and TV personality Paul Hollywood!

Continue reading...

High street betting must clean up its act | Victoria Coren Mitchell

By Victoria Coren Mitchell

My column about the evils of fixed-odd betting terminals brought almost universal agreement but a few voiced their dissent. Let me answer my critics

Since my column last week about fixed-odds betting terminals, I’ve been getting hundreds of messages a day. Ninety-five percent of them agree with my view that these high-stakes modern slot machines should be restricted. I don’t think I’ve ever written a column that encountered more assent – or less dissent – apart from the one about “funeral crashers” who skulked around misleading the bereaved in hope of free booze. Not many people came out in support of those guys.

But FOBT machines are equal to the funeral crashers in their rapacious appetites, and inspired almost as little praise. The unanimity of the response was heartening and baffling at the same time: left-wingers and right-wingers, Labour MPs and Tory MPs, betting-shop workers, betting-shop punters and people who have never been in a betting shop; old and young, the addicts and the free; all united in agreement that the machines should be capped.

Continue reading...

Weekly Update 152

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

I made it out of Vegas! That was a rather intense 8 days and if I'm honest, returning to the relative tranquillity of Oslo has been lovely (not to mention the massive uptick in coffee quality). But just as the US to Europe jet lag passes, it's time to head

Extended Validation Certificates are (Really, Really) Dead

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Almost one year ago now, I declared extended validation certificates dead. The entity name had just been removed from Safari on iOS, it was about to be removed from Safari on Mojave and there were indications that Chrome would remove it from the desktop in the future (they already weren't

Weekly Update 151

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Well that's Vegas done. 8 days of absolutely non-stop events that's now pretty much robbed me of my voice but hey, I got a flying cow! Scott and I both spent BSides, Black Hat and DEF CON doing "hallway con" or in other words, wandering around just meeting people. The

Weekly Update 150

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Vegas! I'm a bit late with this week's update but I thought I'd catch up with Scott Helme and do the video together. We're talking about the events in Vegas, the ongoing Project Svalbard process, some very screwy messaging about certificates from Sectigo and the Irish government coming on board

Welcoming the Irish Government to Have I Been Pwned

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Over the last year and a bit I've been working to make more data in HIBP freely available to governments around the world that want to monitor their own exposure in data breaches. Like the rest of us, governments regularly rely on services that fall victim to attacks resulting in

Weekly Update 149

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

What. A. Week.

I've been in San Fran meeting with a whole bunch of potential purchasers for HIBP and it's been... intense. Daunting. Exciting. It's actually an amazing feeling to see my "little" project come to this where I'm sitting in a room with some of the most awesome tech

Weekly Update 148

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

It's the last one from Norway before heading off to the US and diving into the deep end of the Project Svalbard pool followed by Black Hat and DEF CON in Vegas. That's off the back of the last week being focused on pushing out Pwned Passwords V5, loading several

Authentication and the Have I Been Pwned API

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

The very first feature I added to Have I Been Pwned after I launched it back in December 2013 was the public API. My thinking at the time was that it would make the data more easily accessible to more people to go and do awesome things; build mobile clients,

Weekly Update 147

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

So "Plan A" was to publish Pwned Passwords V5 on Tuesday but a last-minute check showed control characters had snuck in due to the quality (or lack thereof) of the source data. Scratch that and go to "Plan B" which was to push them out today but a last-minute check

Pwned Passwords, Version 5

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Almost 2 years ago to the day, I wrote about Passwords Evolved: Authentication Guidance for the Modern Era. This wasn't so much an original work on my behalf as it was a consolidation of advice from the likes of NIST, the NCSC and Microsoft about how we should be doing

Weekly Update 146

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

After a very non-stop Cyber Week in Israel, I'm back in Oslo working through the endless emails and other logistics related to Project Svalbard. In my haste this week, I put out a really poorly worded tweet which I've tried to clarify in this week's video. On more positive news,

Microsoft MVP Award, Year 9

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

I've become especially reflective of my career this year, especially as Project Svalbard marches forward and I look back on what it's taken to get here. Especially as I have more discussions around the various turning points in my professional life, there's one that stands out above most others: my

Welcoming the Austrian Government to Have I Been Pwned

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Early last year, I announced that I was making HIBP data on government domains for the UK and Australia freely accessible to them via searches of their respective TLDs. The Spanish government followed a few months later with each getting unbridled access to search their own domains via an authenticated

Weekly Update 145

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

Something totally new this week - Israel! I spent the week in Tel Aviv at Cyber Week, a massive infosec conference where I shared the keynote stage with an amazing array of speakers including many from three letter acronym departments and even PM Benjamin Netanyahu. It's funny how on the

Weekly Update 144

By Troy Hunt

Presently sponsored by: strongDM-see why Splunk's CISO says "strongDM enables you to see what happens, replay & analyze incidents. You can't get that anywhere else"

So first things first - my patience for the Instamics we're wearing just reached zero. One of them recorded and one of them didn't which means we've had to fallback to audio captured by the iPhone I was recording from so apologies it's sub-par. I ended up just uploading the

Delayed Spending Review will ‘almost inevitably’ impact housing supply, NHF warns

By Lucie Heath

Delaying the multi-year Spending Review will “almost inevitably” impact the number of affordable homes housing associations build, the National Housing Federation (NHF) has warned.

Morning Briefing: reaction to children living in shipping containers continues

By Luke Barratt

The coverage of a report finding that thousands of homeless children are living in shipping containers and office blocks continues

Harlow Council leader hits out at government over office-to-resi conversions

By Lucie Heath

The leader of Harlow Council has called on the government to change its permitted development rights, after a report from the Children’s Commissioner revealed how office-to-resi block conversions are impacting homeless families.

London housing association borrows £150m from North American investors

By Luke Barratt

Large housing association One Housing has borrowed £150m from North American investors.

Young people like me should be listened to on housing

By Zainab Agboola

Zainab Agboola, a 13-year-old graduate of Phoenix Community Housing’s school of social housing, explains why young people should be taken seriously by the sector

Does the Crewe care home fire bring timber frame construction back into the spotlight?

By Lucie Heath

Two weeks ago, a fire ravaged a care home in Crewe. We look at what happened and why the focus may once again be put on timber frames

Government encourages councils to use powers to sidestep combustibles ban

By Luke Barratt

The government has been accused of “abdicating responsibility” after it encouraged local authorities to circumvent the ban on combustible materials in certain circumstances, Inside Housing can reveal.

130,000 families living in one-bed flats, says NHF

By Nathaniel Barker

More than one in ten children in England are living in overcrowded homes, with 130,000 families squeezed into one-bedroom flats, the National Housing Federation (NHF) claims.

NIFHA calls for UK government to pass reclassification bill ‘urgently’

By Nathaniel Barker

Housing associations have called on the new Northern Ireland secretary to pass legislation to stop them being moved to the public sector “as soon as possible”.

Temporary accommodation has become institutionalised

By Jules Birch

There are few signs of improvement on the horizon when it comes to families in temporary accommodation, writes Jules Birch

Construction industry calls for improved post-Grenfell competence standards

By Luke Barratt

The construction industry has called for competence standards to be improved in relation to high-risk buildings, in a report published as part of its post-Grenfell response.

Housing crisis not caused by lack of supply, says Tony Blair Institute

By Nathaniel Barker

300,000 new homes a year would not make a significant impact on house prices and rents, the Tony Blair Institute for Global Change has said.

Morning Briefing: homeless children housed in shipping containers

By Lucie Heath

A report reveals that homeless children are being housed in shipping containers, the dire living conditions for asylum seekers in London are exposed, and all your other major housing stories

Installing sprinklers is a significant challenge – and communicating with residents is key

By Mark Gardner

Minimising disruption and communicating with residents while installing sprinklers is crucial to making the process a success, writes Mark Gardner

Equalities watchdog reveals 10 social landlords that filed gender pay reports ‘late’

By Kate Youde

Ten social housing landlords were recorded as publishing their gender pay gap reports late to the government in the past two years, according to the watchdog that enforces compliance with the regulations.

Brazil environment minister heckled over Amazon fires

Ricardo Salles is booed after figures suggest a record number of fires are burning in Brazil.

TalkTalk's voice-over is writing speeds that its text can't match: Ad pulled from broadcast

By Kat Hall

Mere threat of time on naughty step has ISP redrafting

An advert for TalkTalk broadband has been taken down for promoting incorrect speeds, The Register can confirm.…

GCSE results: Pass rates and top grades edge upwards

The GCSE pass rate and top grades edge up slightly, despite concerns about exam difficulty.

Fly-half Byrne to make full Ireland debut against England

Leinster fly-half Ross Byrne will make his full debut for Ireland in Saturday's World Cup warm-up game against England at Twickenham.

Brexit: Macron tells PM renegotiating deal 'not an option'

The leaders will meet hours after the French president insisted the Brexit deal could not be reopened.

UK migration: EU immigration fall 'driven by workers'

The number of people coming to the UK from the EU for work has halved since 2016, official figures show.

Brit rocketeer Skyrora reckons it'll be orbital in 3 years – that is, if UK government plays ball

By Richard Speed

El Reg speaks to small-sat upstart in Edinburgh

Interview  With two launches of the SkyLark Nano under its belt, Skyrora aim to go orbital in the coming years - assuming UK Parliament keeps up. The Register had a chat with the Brit rocketeer at its Edinburgh HQ to learn more.…

Ryanair flights take off despite pilots' strike

UK pilots at the airline have begun a two-day walkout in a row over pay and conditions.

Kia Super League: Danni Wyatt scores 59 from just 33 balls as Vipers beat Diamonds

Danni Wyatt top-scores for Southern Vipers with a fine 59 from just 33 balls as the Vipers held on to beat Yorkshire Diamonds by three runs in the Kia Super League.

Ulster Grand Prix safety money spent on club facilities

Three riders have died since quarter of a million pounds was allocated to the Dundrod circuit in 2017.

Ex-England rugby union international Haskell signs as MMA fighter

Former England rugby union international James Haskell signs a deal to fight with mixed martial arts promotion Bellator MMA.

Andy Murray to play in Rafael Nadal's ATP Challenger event

Andy Murray will play his first ATP Challenger Tour event for 14 years in Mallorca next week.

Taylor Swift wants to re-record her old hits after ownership row

The star intends to re-record her back catalogue after it was bought by Justin Bieber's manager.

Gartner awakens from trance, tells huddled villagers: 5G revenue will almost double to $4.2bn next year!

By Kat Hall

It has been foretold!

IT prophesier Gartner has performed its ritual reading of market omens to foretell next year's global 5G network infrastructure revenue. The hype wizards reckon that in 2020 the market will hit $4.2bn, an 89 per cent increase from 2019 revenue of $2.2bn.…

GCSEs: StudyTube revision videos got me through exams

How a community of online students on YouTube helped people studying for their exams.

Three Ways RUM is the Foundation of All Web Application Monitoring

By Matt Pitcher

If a tree falls in the woods, does anyone hear it? If no one visits your website—the web application critical to your business—does it really exist? And if they do visit, their actions there determine the value of what you’ve built. And if it doesn’t matter, your site won’t exist for long, because the investments […]

The post Three Ways RUM is the Foundation of All Web Application Monitoring appeared first on Pingdom Royal.

Subsume JSON a.k.a. JSON ⊂ ECMAScript

Subsume JSON a.k.a. JSON ⊂ ECMAScript

TIL that JSON isn't a subset of ECMAScript after all! "In ES2018, ECMAScript string literals couldn’t contain unescaped U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR characters, because they are considered to be line terminators even in that context."

Via @mathias

Quoting Alex Russell

Y'all decided you could send 6x as much script because the high-end could take it...but the next billion users can't. There might have been budget for 2x, but not 6x. Not by a long shot.

Alex Russell

OPP (Other People’s Problems)

OPP (Other People’s Problems)

Camille Fournier provides a comprehensive guide to picking your battles: in a large organization how can you navigate the enormous array of problems you can see that you'd like to fix, especially when so many of those problems aren't directly in your area of control?

Via @ossanna16

Don’t Worry About an OMG When You’re OOO

By pingdom

Work-Life Unbalance Stress is at an all-time high in the workplace and at home. It’s hard to leave your stress at the office when you bring your computer home or have email, Slack, and more on your phone. An article in Forbes on January 19 shared a study by the Korn Ferry Institute, revealing that […]

The post Don’t Worry About an OMG When You’re OOO appeared first on Pingdom Royal.

Quoting Aja Hammerly

This is when I pull out “we don’t do that here.” It is a conversation ender. If you are the newcomer and someone who has been around a long time says “we don’t do that here”, it is hard to argue. This sentence doesn’t push my morality on anyone. If they want to do whatever it is elsewhere, I’m not telling them not to. I’m just cluing them into the local culture and values.

Aja Hammerly

Optimizing for the mobile web: Moving from Angular to Preact

Optimizing for the mobile web: Moving from Angular to Preact

Grubhub reduced their mobile web load times from 9-11s to 3-4s by replacing Angular with Preact (and replacing other libraries such as lodash with native JavaScript code). The conversion took 6 months and involved running Angular and Preact simultaneously during the transition - not a huge additional overhead as Preact itself is only 4KB. They used TypeScript throughout and credit it with providing a great deal of confidence and productivity to the overall refactoring.

Working with many-to-many relationships in sqlite-utils

Working with many-to-many relationships in sqlite-utils

I just released sqlite-utils 1.9 with syntactic sugar support for creating many-to-many relationships for records stored in SQLite databases.

Via @simonw

Logs vs. metrics: a false dichotomy

Logs vs. metrics: a false dichotomy

Nick Stenning discusses the differences between logs and metrics: most notably that metrics can be derived from logs but logs cannot be reconstituted starting with time-series metrics.

Via Charity Majors

Quoting Daniele Procida

Documentation needs to include and be structured around its four different functions: tutorials, how-to guides, explanation and technical reference. Each of them requires a distinct mode of writing. People working with software need these four different kinds of documentation at different times, in different circumstances - so software usually needs them all.

Daniele Procida

6 Issues a Website Error Checker Can Identify (And You Should Address)

By Ben Lloyd

Most website elements, such as scripts, images, and videos, have either direct or indirect impact on web performance. Reduce the number of errors and optimize the underlying elements correctly, and you’ll significantly improve the availability, performance, and profitability of your online operations. A website error checker is an invaluable tool for helping you get a […]

The post 6 Issues a Website Error Checker Can Identify (And You Should Address) appeared first on Pingdom Royal.

PyPI now supports uploading via API token

PyPI now supports uploading via API token

All of my open source Python libraries are set up to automatically deploy new tagged releases as PyPI packages using Circle CI or Travis, but I've always get a bit uncomfortable about sharing my PyPI password with those CI platforms to get this to work. PyPI just added scopes authentication tokens, which means I can issue a token that's only allowed to upload a specific project and see an audit log of when that token was last used.

Via @brainwane

Free software activities in July 2019

Here is my monthly update covering what I have been doing in the free software world during July 2019 (previous month):


Reproducible builds

Whilst anyone can inspect the source code of free software for malicious flaws almost all software is distributed pre-compiled to end users. The motivation behind the Reproducible Builds effort is to ensure no flaws have been introduced during this compilation process by promising identical results are always generated from a given source, thus allowing multiple third-parties to come to a consensus on whether a build was compromised.

The initiative is proud to be a member project of the Software Freedom Conservancy, a not-for-profit 501(c)(3) charity focused on ethical technology and user freedom. Conservancy acts as a corporate umbrella, allowing projects to operate as non-profit initiatives without managing their own corporate structure. If you like the work of the Conservancy or the Reproducible Builds project, please consider becoming an official supporter.


This month:

I spent significant amount of time working on our website this month, including:

I also made the following changes to our tooling:

diffoscope

diffoscope is our in-depth and content-aware diff utility that can locate and diagnose reproducibility issues.


strip-nondeterminism

strip-nondeterminism is our tool to remove specific non-deterministic results from a completed build.

Debian LTS

This month I have worked 18 hours on Debian Long Term Support (LTS) and 12 hours on its sister Extended LTS (ELTS) project.


Uploads

I also made "sourceful" uploads to unstable to ensure migration to testing after recent changes that prevent maintainer-supplied packages entering bullseye for bfs (1.5-3), redis (5:5.0.5-2), lastpass-cli (1.3.3-2), python-daiquiri (1.5.0-3) and I finally performed a sponsored upload of elpy (1.29.1+40.gb929013-1).


FTP Team

As a Debian FTP assistant I ACCEPTed 19 packages: aiorwlock, bolt, caja-mediainfo, cflow, cwidget, dgit, fonts-smc-gayathri, gmt, gnuastro, guile-gcrypt, guile-sqlite3, guile-ssh, hepmc3, intel-gmmlib, iptables, mescc-tools, nyacc, python-pdal & scheme-bytestructures. I additionally filed a bug against scheme-bytestructures for having a seemingly-incomplete debian/copyright file. (#932466)

Repository driven development

Repository driven development

I'm already a big fan of keeping documentation and code in the same repo so you can update them both from within the same code review, but this takes it even further: in repository driven development every aspect of the code and configuration needed to define, document, test and ship a service live in the service repository - all the way down to the configurations for reporting dashboards. This sounds like heaven.

Via @amatix

Using memory-profiler to debug excessive memory usage in healthkit-to-sqlite

Using memory-profiler to debug excessive memory usage in healthkit-to-sqlite

This morning I figured out how to use the memory-profiler module (and mprof command line tool) to debug memory usage of Python processes. I added the details, including screenshots, to this GitHub issue. It helped me knock down RAM usage for my healthkit-to-sqlite from 2.5GB to just 80MB by making smarter usage of the ElementTree pull parser.

Productivity and the Workweek

Comments

The body is the missing link for truly intelligent machines

Comments

Collect Your Company Knowledge on Autopilot with OneBar

Comments

How to Start with Machine Learning

Comments

Why All of Our Games Look Like Crap

Comments

Using TypeScript with React

Comments

The Most Dangerous Writing App

Comments

Why Japan’s ‘shūkatsu’ job-seeking system is changing

Comments

Ray tracing in Excel

Comments

How Men’s Bodies Change When They Become Fathers

Comments

Making containers safer

Comments

Why the U.S. Army Owns So Many Fossils

Comments

DNS-on-Blockchain is the next step after DNS-over-HTTPS

Comments

After September 18th, you’ll no longer be able to send messages on YouTube

Comments

Preindustrial workers worked fewer hours than today's (2009)

Comments

Montgomery Brewster's 'None of the Above' would walk this election.

By Jackart (noreply@blogger.com)

It's actually quite liberating to follow politics without a team to shout for. I remain a Conservative by inclination. I like free markets, economic liberalism and so forth even if the Conservative manifesto doesn't seem to all that much, Tories, if not their leadership, are mainly for these things. I am also a social liberal, I remain committed to an open and tolerant society. However the Liberal Democrats risk becoming the Church of England does Politics, being stuffed with the kind of dry, shabby inadequate who can't quite get over his (self) loathing of homosexuality. I dislike May. I think she's a narrow-minded provincial bigot who's been promoted way, way above her level of competence. She is however the best of the two candidates for Prime Minister. 

Let's not pretend Corbyn was doing other than palling around with the IRA in the 1980s because the glamour of "anti-imperialist" terrorists excited him. He has always supported whoever was fighting the UK at the time, and doesn't deserve to be an MP, let alone to reverse those letters. Labour's clown-car economics is only marginally less risible than the Tories offer, this time round. The difference is Labour actually believe their silliness, and they're led by a traitor. 

If you live in Scotland, this election is about independence. If you live in NI, then this election is about the tribal headcount. If you live elsewhere this election is whether you want an incompetent nanny-state provincial Tory or an antediluvian Socialist to deliver Brexit. It's a shabby, and dispiriting affair. If you can't work out how to vote, you can always vote for Montgomery Brewster. None of the above is appealing. But if you feel you MUST vote, then I have prepared a handy flow-chart to help you.

If you despise politicians, you get despicable politicians.
This shabby parade of also-rans from which we have to choose on today (without any actual choice on the main, nay only, issue of the day) is the logic of calling decent, capable people like Blair, Cameron and Major "war criminals" and "Traitors", for decades. It pollutes the language for when you actually get some of these things on the offer.
No worthwhile people will put up with the scrutiny and abuse heaped daily on politicians. So you get the kind of bore for whom the scrutiny isn't an issue. They've never done anything interesting in the their lives. At least David Cameron dropped some E and went to a rave or two as a youth. What does Theresa May, who spent her twenties complaining about the promotion of lesbianism in schools, know of fun? As for Corbyn, he looks like the kind of man for whom a perfect saturday night is treatise on Marx (so long as it contains nothing he doesn't already know and agree with) with some lovely mineral water. He is the Labour man Orwell warned you about.

I'll be voting Tory. Why? My local headbanging Leadsomite hard-brexiter has stood down after his colossal act of vandalism, to be replaced by a man with whom I seem to agree.
My expectations are of a  Tory majority around 75, on a low turnout, and they will have half a dozen seats in Scotland.  The Liberal Democrats will take Vauxhall and Twickenham, losing in Sheffield Hallam (the "were you up for...?" moment as Clegg loses his seat), but holding Orkney and Shetland against the SNP, remaining about where they are now overall. Or that's where my betting is at the moment.
What do I want to see happen? I'd like to see May remain PM but in a hung parliament, reliant on Northern Irish politicians for her majority because let's face it, she deserves nothing better.
A rubbish show all round but at least I can enjoy it, whoever loses.

Whales are more Important to Climate change than Donald Trump.

By Jackart (noreply@blogger.com)

Donald Trump has pulled the USA out of the Paris Climate accord. And I don't think this matters all that much. For a start, the USA's emissions are falling. Mostly this is because coal is being replaced by Natural Gas, but also because people are driving less, in smaller vehicles with ever more efficient engines. The motors driving the west's steady fall in carbon emissions are economic and technological, not political.

Next to the steady decline in carbon emissions from the west, is set the Vast increase in emissions in recent decades from Asia. But this represents billions of people using no net carbon energy, tending crops using animal muscle and burning biomass (and occasionally starving to death) Just a few decades ago, to my meeting an indian chap on Holiday in Stockholm with his family and chatting about cricket while we tried to decipher the train times. The rise of the middle class in India and China is a huge flowering of human potential, even if it comes with soluble environmental problems.

Anyway, the level of Co2 in the atmosphere is rising, and this is changing the climate. Reducing emissions is a noble aim, but it must not get in the way of developing economies' economic growth. Fortunately, the solution is already with us. Renewable technology is improving. Cars are getting more efficient, and perhaps moving away from fossil fuel (at least directly). And this process will happen in india and China more quickly than in the west beacaue adopting what will be soon proven and cheap technology will enable them to miss whole generations of poluting technologies.

Which brings us to the great cetaceans. The southern ocean is the world's biggest habitat, with the world's shortest food chain, at the top of which sits the largest animal that has ever existed on earth. Phytoplancton bloom, and are eaten by zooplankton, which are eaten by fish larvae and Krill, which are eaten buy just about everything else. The biggest eaters of Krill are the baleen whales which turn five tons of Krill into Iron-rich shit every day. Sperm whales meanwhile are diving to the abysal deep turning several tons of squid into Iron-rich scat, moving nutrients from the deep to the surface. The limiting nutrient at the bottom of the food-chain is iron, so whale faeces fertilise the ocean, and enable more phytoplanckton to grow which absorb Co2 from  the air, much of which falls to the bottom of the ocean as marine snow, and eventually become rock.

But we killed the whales, and when we stopped doing so, they didn't recover as quickly as we hoped. We didn't just kill the Apex predators, in doing so, humanity reduced the Southern ocean's ecosystem's capacity to create life, and absorb Carbon. The southerm ocean may have settled at a lower equilibrium of Iron circulation. The Atlantic on the other hand, which gets tons of Iron from the african deserts every time the wind blows, has seen whale stocks recover better.

Which is why I want to see more research into Iron seeding the ocean, which may give a leg up to Balaenoptera musculus, as well as possibly solving climate change. Climate change is a problem. But while Trump's petulent gesture doesn't help us solve it, nor does it make the problem any harder. Politicians simply matter less than a whale taking a dump.

Why the Blue Passport Matters.

By Jackart (noreply@blogger.com)

People have spent the day on Twitter saying "why does the colour of a passport matter"? While the Daily Express is cheering the return of the Blue Passport to the rafters. For most people capable of abstract thought, this is a mystifying detail, the importance of which to their opponents is utterly baffling. Of course, I am a remain "ultra". But I did swim in the same intellectual Milieu as the Brexity-Trumpkins for decades and know many serious Brexiters personally. Having spend decades rationalising the EU-obsessed madness of the Tory right as a harmless eccentricity that they don't really mean, I do have, with hindsight, some understanding what these creatures think.



Why does the passport matter?

For the Tory Brexiter, the underlying issue is Sovereignty. They object violently, strenuously and on principle to ANYTHING that comes "above" the Crown in Parliament. The jurisdiction of the ECJ is for them, an insult to the courts and other institutions of the UK. The idea is offensive that any law-making organisation, especially one that Jacques Delors told the trades unions is basically for stopping the Tories Torying, could be "supreme" over parliament.

Of course the ECJ mainly deals in trade disputes and represents an international court to settle international issues and ensure consistent interpretation of EU law. It isn't "making the law of the land" and nor is it a "supreme" court in a meaningful way as far as the average citizen is concerned because it doesn't deal with those issues. If you're up in front of the Magistrate for punching a rotter, you're not going to be able to appeal all the way to the ECJ. Criminal law stops with the nation. Appeals of bad people going up to the European court of Human Rights on seemingly spurious grounds get funnelled into this narrative (shhh, I know), so the impression is obtained that "Crazy Euro-Judges" are "over-ruling parliament", and demanding prisoners can vote or should be allowed hacksaws to avoid trampling on "Human Rights" or whatever the tabloid outrage du jour may be. This then reinforces the narrative that the EU is "anti-democratic" and "makes all our laws". And once you have this narrative, flawed as it is, it's jolly easy to amass an awful lot of corroborating "evidence" because the Tabloids spent 30 years deliberately feeding it.

Sovereignty vs Influence; there is a trade-off. The UK, broadly, wrote the Financial services legislation for the entire continent. In return, the Continent got access to the only truly global city in Europe. The French did this for farming and got the CAP, while the Germans got the Eurozone's interest rates and got to destroy Southern Europe. The EU which contains (rather like the UK and trade negotiators) no-one who CAN write decent financial services legislation legislation, because most of those people are British. Thanks to Brexit, the quality of the legislation on financial services will go down, both in the UK which will be compelled to have regulatory equivalence to keep banks' access to the single market and the EU. The UK will have become a rule-taker rather than a rule maker. I fail to see how this reclaims "Sovereignty". The organisational source of the legislation will remain unchanged, but we loose any ability to influence, let alone write it. Multiply this catastrophe across an economy and you see why the "sovereignty" argument against EU law is, on any rational basis, stupid.

The parliament, the very existence of which takes on the aspect of a supranational government in waiting, rather than a simple means to have democratic oversight of an organisation which employs fewer people than Manchester city council, distributes about 1% of GDP and writes trade law. This unwarranted grandiosity once again suits both the Brussels apparatchiks, and the simian oiks of UKIP whom the British public sent to Brussels as a mark of the National contempt for the institution. The parliament is, to my mind is a risible little potempkin affair, barely worth considering,

So there's the error. Back to the passport.

The International Civil Aviation Organisation sets the dimensions, so the writing was on the wall for the old British hardback passport, fabulous though it was, it didn't really fit in the back pocket of your trousers.  However once you believe that the EU tentacles are slowly creeping into institutions to turn you into a province of the "EUSSR", then you start to see this everywhere. The EU is foolish to seek the trappings of a national Government before they had built a demos, and absent any desire for it from the people. Symbols matter. The UK doesn't have an ID card. So when Brits talk about nationality they might say "Australian passport-holder" rather than "Australian citizen". I am not sure if any other nationalities use this formulation. The passport is slightly more than a document. No? Try losing one abroad.

The EU resolution on Passports is here. For anyone who thinks the EU "made" the UK have a Maroon passport, here's EU Croatia's. .



The EU suggested the Colour be harmonised and the words "European Union" be put First. At the top. Above the crown, First. Symbolising, perhaps inadvertently that the EU was more important than the nations. And there you have it. And no-one working on it thought to object. Changing the colour of the passport was a key symbolic gesture that irritated many people, and reinforced an utterly false narrative, to no end or benefit to anyone. There is simply no need for European Union passports to be uniformly coloured. It merely satisfies the bureaucrats' desire for order. And it is my belief that it is this symbolic bureaucratic exercise in territory marking by the EU that revealed, and still reveals, a fundamental disconnect between the Brussels Panjandrums, the people of the EU and the British in particular. The Eurocrats want a Federal Europe with the EU as a Government. The Nations, broadly supported by their governments don't, and have resisted any attempt.

The EU hasn't made Britain less "sovereign". All EU law, necessary to trade with as little friction as possible, is of the type that by whom it is written doesn't matter. With trading standards does it really matter WHAT they are, just that they're as universal and consistently applied? I don't need to tell you that it was never illegal to display prices of potatoes in Lbs and Oz, just that you HAD to display the price in KG and g too, in case any Frenchmen walking through the market didn't know how many Lbs are in a KG. I don't care who writes the regulations for the import of Duck eggs, just that it's done.

But there it is. The Brexiters shooting with the accuracy of a semi-trained recruit who's just dropped LSD at every figment of their fevered imagination, egged on by equally deluded fantasists who still think they're creating a Federal United States of Europe. These two groups of lunatics needed each other. And so, the passport, with 'European Union' at the top was barely noticed on the continent, but seemed to some Brits as evidence the EU was after their democracy, their identity and their Freedom. However stupid this belief is, a Blue passport could've been delivered cheaply as a quick Tabloid-Friendly win for Cameron and such was the narrow margin, it would have probably been enough.

Rosencrantz and Guildenstern are Dead.

By Jackart (noreply@blogger.com)


Wednesday saw my 40th Birthday, and to celebrate I went to see Tom Stoppard's brilliant Rosencrantz and Guildenstern are Dead at the Old Vic with a Chum. While Daniel Radcliffe & Joshua Maguire lead, the show is stolen by a magisterial performance by David Haig as The Player, a sort of luvvie-pimp-cum-impresario who holds the whole play, in its absurdity, together.


The play is Hamlet, seen from the point of view of two minor characters, Rosencrantz and Guildenstern, old friends of Hamlet's. The hapless pair spend the play wondering what they're doing and why, having been recalled to Elsinore by Claudius to find out why Hamlet's being such a dick, moping about and talking gibberish to himself ("to be, or not to be..." etc). They are eventually betrayed by their friend, who suspects them of working for his uncle which they are, sort of.

The play is therefore a meditation on the futility of existence, and the limitations of people's personal agency. Most people get on with their lives, as bit parts in a greater drama, not really sure as to the direction of events, or even of the past. After all, what have Rosencrantz and Guildenstern got to go on, but what can be gleaned from a few words of Shakespeare's, as metaphor for everyone's flawed and self-serving memory. Any interrogator or detective will tell you about the reliability of eye-witnesses and the difficulty of establishing the truth.

From everyone's point of view then, even when we're at the centre of events, most of the action is happening offstage. There will have been some point at which you could have said "no", but you missed it. Then you die.

If you can get tickets, do so.

Minimum Wages, Immigration, Culture and Education.

By Jackart (noreply@blogger.com)

Net migration to the UK has run at hundreds of thousands a year for decades, of which about a quarter since 2004 has been "A8 countries", Poland, the Baltic states, Czech Republic, Slovakia, Slovenia and Hungary, another quarter from 'Core EU' and the rest from non-EU countries, mainly India, Pakistan and West Africa. 13% of the population of the UK was born overseas, of whom over 2/3rds are non-EU migrants. This is an unprecedented migration to the rich world from the poor, and It's not clear from this EU migration is the underlying problem. The Poles will integrate fast, and leave imprints on the culture like a higher incidence of catholicism, bigos (a stew of meat and Sauerkraut) and some hard-to-spell surnames. They're often better educated than the natives, and work harder.

In general the view I've taken over the years is that minimum wages are a bad thing, arguing that they are mainly paid for by the people who otherwise wouldn't get a job at all. Only a job can lead to a better job, and if people are unemployed for a long time, they often become unemployable. So by this logic, keeping unemployment down should in the long-run be better for the poorest.

But, there is a trade off. When I grew up, late '80s and '90s, I cannot recall seeing cars washed by hand. When my father wasn't exploiting child labour by getting me and my brother to do a rubbish, half-arsed job for which we expected to be paid handsomely, we went to see the "blue Dougals" at the petrol station. The UK as a wealthy country, had substituted Capital for Labour, and cars were washed by big machines at every petrol station. But a team of a dozen hard-working and cheerful eastern Europeans can set up a car-wash, do inside and out for very little capital outlay - a jet washer, and some sponges, so when the EU accession countries citizens moved to seek work, this is what many did. The car wash machines were gradually removed and replaced by people. This is the opposite of progress.

Let's take a step back and look at the big picture.

Europe's wealth, it's vitality, its progress didn't spring from European individual or cultural superiority. It started when half the population was wiped out by Yersinia pestis in the 14th Century. There was a certain amount of luck - the same event increased the power of the landowner in Rice states and in pre-feudal societies farther East, but in Northwestern Europe, this created a shortage of Labour, and the peasants rose up a generation afterwards to demand higher wages from their lords. When this happened in Italy, the energy was put into sculpture of the nude male form, and was called "the Renaissance". When wages rise, it makes sense to build machines rather than employ labour, which has a virtuous feedback loop: skilled people running the machines drive up production, and become richer, which creates an incentive for further innovation. More widespread desire for, and access to education is grease in the wheels of this, the motor of progress that led to the industrial revolution.

The opening up of America, a nation with a perpetual and long-lasting shortage of labour not only added another motor to that European culture of innovation which grew up after the Black Death, but also absorbed the excess labour of Europe. While there is a labour shortage, immigration can be managed, though immigrants in large numbers have nowhere, ever been welcomed by the people they move to. Even when the people are kith and kin, the 'Scots Irish' (in reality, families originally from Northern England and the Scottish Borders) were moved on by the Germans and English who'd already settled the East coast. They ended up in Appalachia.

It's clear, then in the short run and in aggregate, wages aren't "driven down" by migration in a market economy. Part of that, in modern times may be due to the minimum wage, which protects some of the people most vulnerable to substitution, but also the 'lump of Labour fallacy'. Immigrants, especially young workers with families bring demand as well as supply and these things more-or-less balance. They aren't "taking our jobs" but they are changing the nature of jobs available. And the vast supply of excess labour from the subcontinent, africa and the poorer bits of Europe is not exactly an incentive to invest in productivity-enhancing machines, as the car-wash example shows. The mass immigration from the poor world has the potential to stall the western motor of innovation and may contribute to wages not rising as far as they might, especially for the lowest skilled workers.

The UK has a problem with productivity. UK employers have got good at employing the excess Labour of a serious chunk of the world, UK wages have been flat for a decade, and these things are linked. So the Chancellor is hiking the minimum wage in the hope of good headlines, and to incentivise investment to drive productivity. So. What effect will this have on immigration. Will it draw more migrants to the UK hoping for higher wages, like European immigration to the USA, or will it price low-skilled immigration out of the Labour market and allow the motor of progress to continue?

Splits that used to be geographic - some countries were rich, and others poor and the movement between the two was rare, is moving to one where there are still two countries, it's just the divide is social, educational, and cultural. You have a global, liberal, free market culture, which values education and novelty. And you have national, 'c' conservatives who just want their own culture, don't care about education all that much, won't move to find a job, and expect to be looked after who stay put and resent incomers. And the latter are disproportionately annoyed about foreigners moving into "Their" neighbourhoods while it's the former who have more to fear in the short term from highly skilled competition, minimum wages see to that. And if minimum wages rise far enough, low skilled workers will not be able to get jobs and they will stop coming to the UK. The problem is, the lowest skilled people are often native. The cost of a raised minimum wage will be borne by those least able to cope.

If we are to avoid society fracturing permanently into Morlocks and Eloi we do need to manage migration, to keep that motor humming. We cannot let the world come at will. But there was no need to pull up the drawbridge against EU migrants who always looked like collateral damage to me.

It's not all about economic self-interest, nor is it wholly naked in-group preference (what educated, open minded people call "bigotry"). It is the interplay between the two. Ultimately the stagnation of UK wages over the last 10 years isn't due to migration, but the recovery from a balance-sheet recession of 2007-9. It's the feeling of ennui caused by a decade of stagnation which has caused the anti-immigration nonsense, the rather blameless Poles have just become a Piñata and for a population that was persuaded to lash out at the EU when they really wanted to lash out at "the Muslims". The tragedy is all this happened just as we were getting back to normal.

Nicola and Theresa. Phwooar.

By Jackart (noreply@blogger.com)

The Daily Mail's headline "Legs-it" about Scottish First Minister and British Prime Minister Theresa May's shapely legs was pathetic. But remember, the Mail is written by women, for women, and women judge each other, all the time, harshly and vindictively. Judged especially harshly are women more accomplished or better looking than the average Daily Mail reader.


To call this "sexism" is to miss the point. This isn't about women being held down by sexist male tittle tattle. Clearly, two of the most powerful people in the country haven't been held down in any meaningful way. Any executive head of Government is fair game for any and all criticism. What these women have done is rise above the level at which society normally seeks to protect women from abuse.

Male politicians are made fun of for their appearance and clothing all the time. It's the sea men swim in. Whether it's Donald Trump's expensive, but ill-fitting suits and too-long ties like he's stepped out of a 1980s pop video caricature of a businessman, or Cameron's forehead, or the fact that middle-aged men are always assumed to be repulsive, this abuse is normal.  The ridicule a male politician faces when he's seen in public wearing anything other than a blue suit is extraordinary. From Tony Blair wearing a clean barbour, to William Hague's baseball cap or Cameron's beachwear, there's a reason male politicians dress identically. When women's clothing (far more interesting by the way, than the sober suits of most male politicians) is commented on, it enables a personal brand to be created that much easier. Theresa May's shoes are like Margaret Thatcher's handbag. True, women do have to think harder about their clothing - too much leg, cleavage etc... and you immediately invite scorn (of other women, mainly), but the fact the female wardrobe stands out against the endless blue/grey suits and red or blue ties of the male is as much an opportunity as it is a minefield.

Any comment about May's shoes, for example is part of her deliberately curated brand, and shoe-designers are falling over themselves to get their products onto her feet. This isn't sexist. Women like shoes, and there's no reason why Theresa May shouldn't have fun with them.

Lower down the pecking order there's a taboo against men commenting negatively on a woman's appearance, lest you hurt the poor dear's feelings. Yes male 'locker room' banter will discuss who's attractive, but it's rude to do so in front of women and by and large, gentlemen don't. Women don't typically have these conversations about men in earshot of men either, but describing men as "revolting" or "creepy" is so normal as to be unworthy of comment, and completely unnoticed. May and Sturgeon have risen above this social protection, and are subject to the same rules of engagement as men are. i.e that if we have feelings, tough.

These women are grown-ups doing important jobs. If you think the Mail's light-hearted front page is an insult to them, you're an idiot. Of course Sarah Vine who wrote the thing, knows exactly the response it would get, howls of idiot outrage from the usual suspects on Twitter, and from Sturgeon herself. This allows the paper to swat the complaints aside with contempt. This signals to their readership that the Mail is on their side against the bien-pensant left with their idiotic & totalitarian outrage about human trivialities. May by rising above it, does the same. The Mail is one of the Best-selling papers in the UK, and one of the world's most visited "news" (ish) websites. Who won that exchange?

The po-mo left, obsessed with identity politics, used to being able to bully dissenting opinion down STILL hasn't got the new rules of the game. Someone's pointed out the Emperor's naked, but he's still acting like he's in charge and hasn't noticed the mood's changed. Yet.



Completely unrelated, but thank you to the Anonymous commenter who wrote this. It cheered me up.

On Class, Culture and the New Politics

By Jackart (noreply@blogger.com)

The two tribes of politics, broadly the Tory and Labour parties divided over the 20th Century principally on the matter of economics. Simplifying: Tories preferred market solutions to state planning, and preferred lower taxes and less generous state spending.
The Labour party, which when it abandoned clause IV, surrendered on the economic question, not coincidentally a few years after the Berlin wall came down.
As a result, the great battles since then have been essentially cultural. Gay rights, racial integration etc. The confusion stems from there being no consensus within the Tory or Labour tribes on these issues. Plenty of Tories are happily socially liberal, many of the Labour tribe are socially conservative, especially when you look at voters rather than representatives.
Which brings us to the tribal division of Britain: class. The middle class: liberal, internationalist, universalists; vs a working class: authoritarian, insular and particular world view. The former is comfortable with diversity and immigration. The latter isn't. The former's kids live a long way from home, and move for work, the latters kids live in the same town and expect the work to come to them. The former don't speak to their neighbours, the latter care what their neighbours do and think. These labels are correlated roughly with, but independent of, economic status. It's possible to be middle class, in a local-authority home living on benefits, and working class, earning seven figures and living in a manor house. (Though it's likely these people's kids will change tribes)
There are elements of these cultures in all major parties in the UK, but the rest of us rarely communicate with people from the other tribe. The people you have round for dinner will most probably be from your tribe. Half the country holds its knife like a pen, yet none have sat round my table. When the two tribes meet, it's awkward. Those difficult bottom-sniffing conversations seeking common ground are easy to conclude when two members of the same tribe meet, and difficult when you meet the other half.
There have always been working class Tories, because much of the working class is as comfortable with the certainties of heirarchy as a shire Tory, and doesn't much care for this freedom and opportunity nonsense, preferring a better boss instead. And it's interesting to watch the Tories dangle the protectionism and insularity the working class has long demanded. Middle class labour fabians and the working class methodists have always sat uncomfortably together. Brexit has shattered that coalition, the labour party has been handed to the idiot socialists and will die, unless somehow moderates can oust corbyn before 2020.
Which brings us to the Tory coalition. The high-Tory have promised the old certainties back to the white working class. Meanwhile, middle-class liberals who make up most of the parliamentary party are distinctly uncomfortable with much of what is being done in Brexit's name, but will stick with the Tories, because they offer the promise of power, and however dreadful Brexit is, Jeremy Corbyn is worse. A new coalition is being forged between the Tory squirearchy, and the Working class based on nationalism, social conservatism and heirarchy, directly taking Labour's core vote. This is why UKIP, a working class movement that thinks it *is* the conservative party, apes the style of a country gent. The working class have always got on well with the Gentry, sharing sociailly conservative values. Both despise the middle class.
Brexit split the country down a line more on class values, split the country and handed it to the socially authoritarian party. Whether this is the new politics, with the Tories moving from being the middle-class party to the working class party, as the Republicans did after the war in the USA, or whether the middle-class will wrest back control over both parties in time waits to be seen.
I suspect unless May softens her tone, and thows some bones to the liberals, her coalition will only survive until there's a credible opposition. A more appropriate division of politics would be a ConservaKIP'ish alliance of WWC and high-tory squires, vs LibLabCon middle-class liberals. Therea May seems to be actively seeking it.
Over the Channel, Emmanuel Macron and Marine Le Pen exemplify this split. The candidates of the parties of left, Socialists; and right, RPR are likely to be eliminated in the first round. Macron is likely to win comfortably. His movement 'En Marche!' was only formed a year ago. There's a lesson for British liberals there.

Tories have profoundly damaged the UK. You Should Join the Tories.

By Jackart (noreply@blogger.com)

2016 happened because decent people don't join political parties, leaving the business of Government to socially inadequate, physically repellent gits with an axe to grind*. In normal circumstances, this makes politics easier for genuinely impressive people to progress through the flotsam of monomaniacs. To be a Grown-up in the Tory Party 1997-2010 was to be able to consider an issue beyond the EU. For Labour it's all about not dreaming of Strike Action by "the workers". Thus the Liberal Centre consolidated a hold on the country, but became complacent to the poison seeping into parties even as the Smug centrist consensus made everyone fat and rich.
There has been a steady, and persistent hollowing out of the political parties. Labour used to be allied to a Trades Union movement that delivered services - health insurance, education and so forth to its members. The Trades Unions of Pre-War Britain where an overwhelming force for good. Atlee's welfare state nationalised all the good the Trades Unions used to do, and so corrupted both the principle of welfare (now far, far from Beveridge's original vision of low, universal payments like Child benefit, topped up with contributory elements) and the Trades unions which became a mere tub-thumper for more state spending. This left the Labour party with the sole purpose of defending a welfare settlement that is not under threat, and a Trades Union movement whose purpose had been nationalised so simply became resistant to all and any reform which might make the system as is function better; unions a mere vested interest of public-sector workers. This isn't a place where people capable of holding more than one idea at a time feel comfortable, and so the Labour party was colonised by people who think not shaving is a political act.
This malodorous and poorly groomed cancer has destroyed the Labour party. It's over, there's no point being in Labour unless you're a Identity politics obsessed Corbynite who laments the end of the Soviet Union. 
Labour, 2010-Present
The Tories at least had the sense to try to vomit the most toxic of their nutters into a bucket marked UKIP, a bucket the dog is unfortunately returning to. The Conservative party my Grandfather joined (from CPGB, as it happens, Labour even back then were cliquey dick-heads) used to be a forum for the upper middle class (and anyone who aspired to join them) to meet, mate and do business. But the horrible young Tories of the '80s, and the Euro-nutters of the '90s meant that by 1997, the Tories were only really suitable for people who were prepared to discuss "Europe" endlessly in ever-more foaming tones, persuading themselves that the EU is a historic enemy like Napoleon, the Kaisar, Hitler or the USSR. To their credit, the Tory Leadership has long known what to do. All David Cameron ever asked of his party was to "stop banging on about Europe". They couldn't stop picking at the scab, and the result is a catastrophe that has already crashed the Pound, weakened the UK (perhaps fatally) and may yet cause a political crisis in Europe and embolden Putin to start rebuilding the USSR.
Tories, 1997-2010
The more say over policy and leadership given to the membership, the more the membership has dwindled (unless, like Labour, the membership criteria are designed to invite entryism for the purposes of choosing a leader - by people who've been quietly loyal to the Bennite project for decades). Giving members a say in who leads the party is absurd. Who the prime minister is, should be a matter for MPs, and MPs alone. It is they who must give the Prime Minister a majority and internal party democracy risks, well, exactly what has happened to Labour. 
However, that Rubicon has been crossed. Party members now expect a vote on the Leader. The question is what to do about this, and the answer is to choose to be a member of a party at all times, hold your nose if necessary. Do NOT identify with the party, but consider which is best placed to advance your objectives. At the moment, the foul bigots, monomaniacs and morons of UKIP are being re-absorbed from a position where they can do little harm beyond foaming at the mouth and masturbating to Daily Express editorials, to one where they can choose the next prime minister, and Mrs May isn't a healthy specimen. The ex-'KIPpers chance may come to choose their PM sooner than expected.
I'm often asked "How come you're still a Tory?"  
Were the Liberal Democrats stronger, I'd be considering them, but I don't trust them on electoral reform (about which they're as silly as Tories are about Europe). But as the Lib-Dems are so far from power, I don't see the tactical benefit of leaving the Tories in a huff, and I broadly agree with the Tories on everything except Brexit. What I'm worried about is the 'KIPpers who're returning to the fold. Unless you want a foul, divisive and ignorant Brexit headbanger to replace May in 2023 or so (Gove for example), Join the Tories, because thanks to Labour's meltdown, Tories and Tories alone will choose the next PM. All not joining a party does is strengthen those (*we) weirdos who still do. Labour moderates, disgusted by Corbyn should cross the floor to the Tories or Liberal democrats, instead of flouncing off to the V&A and opening the way for UKIPish Brexit-o-twats to fight and win a by-elections under Tory colours. Were Tristram hunt now a Tory, not only we could soften this brexit idiocy but also signal just how broad a church the Tories are. 40% of Tory members voted Remain. The tribe that needs to understand the value of a bit of entryism is the liberal centre, who need to abandon any loyalty to their Parties and go to where the power is. The Liberal Centre is complacent because they have for so long occupied the ground sought by all parties, they've not really had to compromise. 
At the moment the business of Government is, and will be for the foreseeable future, a Tory-only affair. That need not look like Nigel Farage, but it will, if Remainers abandon the Tories entirely.

The End of A 'Belle Époque'. 1991-2016.

By Jackart (noreply@blogger.com)

The interlocking webs of policy which 'politics' seeks to knit are complicated. Whole books can be written on how two individual policies interact. PhDs in Economics are awarded for small snapshots of the whole cloth. Most people don't have the time to keep abreast of developments or read sufficient history to understand why some policies are bad. Thus, people use heuristics - rules of thumb - to make decisions  about that which they aren't expert. "Is this person trustworthy" is a key issue, and we tend to overweight the opinion of those near us. "He is my brother, and I say he's ok" says a friend, you are more likely to believe a mutual friend, than the opinion of a stranger on the same issue.

In the evolutionary past, such a question was a matter of life and death. People only really had to trust those with whom they shared a close genetic relationship. Since the development of agriculture, we've been steadily widening that circle of trust. The wider you spread that circle of trust, the richer your society will be. Even before it had a name, Free market economics allowed people to become blacksmiths, knowing others have water, food, shelter and so forth covered in return. More specialisation, greater productivity, means greater wealth.

Eventually, this requires trust in people we've not met. Towns' food supplies require that farmers unknown and distant supply the basics of existence. Nowadays, It's unlikely the west could quickly supply all available plenty currently manufactured in China. Nor could China supply quickly the complex components and tools shipped from Japan, Europe and USA. Both China, and "the west" are richer from the exchange. And yet, we still don't trust "globalisation".

Most persistent fallacies in political economics are the result of simple policies that appeal to some base heuristics, but which when applied to the larger and wider society, fail catastrophically. Thus egalitarianism in one form or another pops up every 3 generations or so and succeeds in making everyone equal, but some more equal than others, and even more, dead. Then nationalism comes along, and says it's all [another, arbitrarily defined group of humans with slightly different modes of speech] fault, leading to more waste and piles of corpses. And even when the results aren't catastrophic, we seek out the views of those who agree with us on say, Nationalism to inform our opinion on, say, whether or not people are responsible for climate change.

Which political tribes stumble into being right or wrong on any given issue appears arbitrary, because no-one's asking for the evidence before they decide on the policy. Instead of asking "what's right", we're asking what's popular (amongst the coalition of tribes that voted for me) right now. That an opponent comes out with an identical policy, for different reasons is reason enough to oppose something, forgetting completely prior support for it. After all, whatever [another political tribe] thinks must be wrong, right.

Thus
The Labour party opposes ID cards. The Labour party has always opposed ID cards. The Tory party is for the Free market and was never in favour of the Corn Laws. We have always been at war with Eastasia. Perhaps if we could think for ourselves rather than just accepting tribal dogma, we'd get better governance. But none of us have the time. So "Democracy" is merely a means to give temporary permission to one coalition of tribes to push through dogmas over many issues, until either the population notices, or the coalition of tribes breaks up, and the electorate takes a punt on the other tribe's prejudices for a bit, and then gets on with whatever they were doing before.

Society ultimately advances by eliminating prejudices it's acceptable to hold thus widening the circle of trust, and increasing riches. By falling back on ancient heuristics to answer the wrong question ("who's fault?" is the wrong question) 2016 democracy has delivered the worst political outcomes on a broad front, as a result of which, we are poorer, and more likely to start fighting as a result of the collapse in political trust we have seen over this year. The post Cold-War 'Belle Époque', which saw half of humanity, 3 billion people, lifted out of poverty, is over.

Idiots cheer.

Boston Dynamics and The Late Sir Terry Pratchett

By Jackart (noreply@blogger.com)

Everyone knows how driverless cars will work: they will be like ordinary cars, except you read a book rather than acting as pilot. And so, people's understanding of what a technology can do is clouded by what the old technology it replaces does. Which means people without imagination, Head of IBM Thomas Watson, for example, say things like
"There may be a world market for maybe five computers"
and get it wrong. In 1943, computers were used for cryptography, and that's it. (At least he knew what a "computer" was, which few did back then). Predictions are hard, especially about the future. But it's probably worth noting here that the famous World Wide What? front page of The Sun, was in fact rather a good a spoof, by The Sun.


Boston Dynamics makes robots.


via GIPHY

Who needs Robots? Well, like computers or the internet or driverless cars, the technology is coming. And it will change people's behaviour in many, unpredictable ways. For example, mobile phones were conceived as portable analogues for the phone on your desk or in your hall. SMS text messaging was added as an afterthought, but became THE dominant means of communication. Calling someone is now rude, often you text first to see if a call would be convenient. Who (apart from mums) leaves voicemail messages any more? Few predicted that change in our behaviour. The smartphone is now ubiquitous, and is more about accessing the internet than calling friends, but wasn't imagined before the internet, Except by Douglas Adams (and John Brunner of whom I'd not heard until I discussed the issue on Twitter). Driverless cars will be as close to the car, as the car is to a buggy and four. And robots, when they become ubiquitous, will be unlike anything we've considered.

I look at Boston Dynamics Robots, the big dog is conceived as a load carrying mule for soldiers on rough terrain, and I think of The Luggage, Rincewind's inscrutable companion on the discworld. I suspect everyone will one day have a robot the size of a dog to carry daily necessaries, following them round. You could send your luggage to someone else, by smartphone app to pick something up. Your luggage could take your shopping home and collect it from the store for you. Large luggages could be sent on ahead with bags. Small luggages could replace handbags and briefcases. The labour and time saving would be vast, spawning whole new areas of employment, servicing and modifying your faithful electronic companion and providing for the opportunities they create to effectively be in two places at once. Freed from the ownership of motor vehicles by the fact we'll be taking taxis everywhere, our Robot luggage will perhaps become the next status symbol around which society is built, replacing the car.

Like cars, I suspect the battery technology will be the limiting step, and like cars, I suspect the fuel cell will be the answer. Small fuel cells will one day power your smart phone too.

But think about the opportunities for people from smart phone. There are tens of thousands of app designers round the world now, a job that had barely been considered as recently as 2007, when the first iPhone was released, and that is similar to how the jobs which will be taken by the robots, will be replaced. That is why people who fear of a "post-jobs" future were wrong in 1816 and are still wrong 200 years later. The world's only limitless resource is human ingenuity.

Anway. I for one welcome our new robot overlords, and this guy should totally be locked up.


via GIPHY

Fidel Castro is Dead. (Some of) his Legacy will Live on

By Jackart (noreply@blogger.com)

Let's be clear, Castro was a murderous bastard who impoverished his country, and whose views on homosexuality and on the importance of brevity in speeches were nothing short of horrifying. It's true, Cubans do have access to better healthcare than many countries of equivalent GDP per capita, and if I had to choose a Communist hell-hole to live in, it'd probably be Castro's Cuba. But the Cuban healthcare system is not the fantasy of western dewey-eyed left-wingers, and Cubans often are excluded from what excellence there is, as it's one of the few means the country has of generating hard currency earnings. Rich foreigners get the best doctors, and more are exported to other successful "progressive" regimes like Venezuela.

"But he was an anti-imperialist". So why were cuban troops in Africa in support of the USSR, which was by any measure or definition an Empire? Anti-Imperialsim is just the justification leftists give for knee-jerk anti-Americanism. And the flood of people risking death to reach the USA should tell you all you need to know about the relative merits of America's and Cuba's system.

Contrasting the attitudes of the USA to Castro, to their attitude to equally murderous bastards like Pinochet misses the point. The US embargo on Cuba is one of the legacies of the Cold war, kept bubbling by the politics of Florida, home to so many Cuban-Americans. There is no Doubt that the US blocade has impoverished Cubans, and that with the fall in the Berlin wall and the collapse of the USSR, such an embargo was no longer justified. However politics are what they are. Fidel Castro's death provides an opportunity for further thawing in relations.

The USA supported "our son of a bitch" all over the world, turning a blind-eye to horrific human rights abuses, though often (albeit less often than we should) working behind the scenes to try and mitigate the worst behaviour. Thatcher is rarely credited with preventing the execution of Nelson Mandela, but she consistently urged Mandela's release, even as she argued against sanctions and branding the ANC "Terrorists". This is one reason why the cold-war piles of dead of Nasty fascist bastards are usually lower than those of nasty communist bastards. I also think the point made by CS Lewis holds. Right wing dictators rarely pretend to be GOOD, making their appeal more on effectiveness.
"The robber baron's cruelty may sometimes sleep, his cupidity may at some point be satiated; but those who torment us for our own good will torment us without end for they do so with the approval of their own conscience."
And one by one, following the collapse of Communism, the support from the USA and its allies for these disgusting regimes was withdrawn. Apartheid South Africa, much of South and Central America saw right authoritarian regimes fall. Genuine democracies were often created in the rubble. The USA didn't support dictators because the USA is an imperialist power, but because it IS a power, and with that comes responsibility. They judged at the time the alternative, Communism, was worse, and represented a genuine existential threat to the USA and its core allies.

This is why for example the USA and its allies mostly support the Regime in the Kingdom of Saudi Arabia. The Saudi regime is repellent, but given the probable alternatives wouldn't be nice, liberal, democratic-minded progressives, they'd be salafist nutcases who'd have access to billions of dollars of oil revenues and the legitimacy of being the Guardians of the Two Mosques. The House of Saud is all that stands between the West and a plausible salafist caliphate with sufficient legitimacy and money to one day threaten the west. We'd rather do business with nice, stable democracies under the rule of law. But seeing as we cannot do to every country on earth what we did to Germany in the late 40s and 50s, we make the best of the options given.

Castro appeared to be a true beleiver in Socialism, so he refused to recognise his philosophy had failed, and his island limped on, a socialist throwback in the age of globalisation. The current poverty of Cuba is partly America's doing, but mostly due to decisions made by Castro himself, policies which set him and the Cuban people at odds with the regional hegemon, in persuit of an evil idealogy. Fidel Castro was on the wrong side of history, and his people suffered because of his stubborness. Now he's dead, it's Cubans turn to make the most of the positive legacy - Cubans are the best-educated poor people on earth, and the mighty economy of the USA is right on their doorstep. There is going to be a lot of money to be made there, and this time, for the first time, Cubans will share in it.

Hail, Trump! God-Emperor of the Alt.Right

By Jackart (noreply@blogger.com)

And Let's be honest, he's ghastly and despite brown-nosing by Nigel Farage, he's no friend of the UK's, because he doesn't value anything the UK brings to the table. Rumour has it, he asked Farage to intervene in an offshore windfarm decision affecting his Scottish interests, which suggests he doesn't understand the concept of 'conflicts of interests' when in elected office.

This further suggests Trump will attempt to use the office of President to enrich himself, rather than doing so after leaving office, as is accepted. All this is rather feudal; the office holder as gold-giver, distributing patronage and receiving tribute. He's an entertainer and showman, which hails to an even older tradition of politics: that of Imperial Rome, where emperors used state coffers to enrich themselves and their clients,while keeping the mob quiet with bread and circuses.

Donald J. Trump is psychologically unsuited to office in a mature democracy. He is thin-skinned, autocratic, insecure, ignorant, and completely without any understanding of the levers of power he now wields. Much like (later caricatures of?) Nero, Commodus or Caligula.


Despite (or perhaps because of) this, the adolescent losers of Alt.Right see Trump as a God-Emperor (no, really they do. Video surfaced today of people making Roman Salutes, saying "Hail Trump", and distribute Memes based on Games Workshop's futuristic figure-based tabletop wargame, Warhammer 40,000 where humanity is defended from Chaos by a psychic God Emperor). If Trump is Imperator, then the Secret Service is a Praetorian Guard. And how did the Praetorian serve Commodus, to pick one example?

Trump might, were he capable of reading a book, muse on the fact he's surrounded by armed men sworn to uphold the Constitution of the United States of America, and defend it from Enemies DOMESTIC and foreign. Thankfully, the USA is a mature democracy. Where once armed men acted as kingmaker, courts now do. For the simple reason Ignorance is no defence, and the fact that Trump's loathing of 'Washington' is fully reciprocated, I find it unlikely that Trump will survive his term alive and unimpeached. Unfortunately I cannot find odds on a Trump impeachment before 2020. Perhaps it's a racing certainty.

Sexism and the Loss Aversion Heuristic

By Jackart (noreply@blogger.com)

Men are physically stronger than women, respond quicker to physical training, and suffer less injury under physical stress. Men are more robust, suffer less morbidity than women in almost all phases of life. Obviously these things exist in a normal distribution, but men's distributions are typically platykurtic - there are more men in the tails of the distribution than women. Thus, even where the means are near identical, such as intelligence, you'd expect to see more male geniuses, and imbeciles among men than women, who're more concentrated around the mean. Feel like taking issue with any of these statements? Then you might as well be a creationist.

Men are more accepting of risk, and will prioritise pay over flexibility. So you'd expect men to make up the majority of soldiers and miners and race car drivers. It also means you'd expect to see more men make up corporate boards, everything being equal. More men are more drawn to the cut and thrust of business, and are more likely to prioritise work over other commitments. Women value stability and flexibility more highly than men. This means women, on average don't choose to make the effort necessary to climb the greasy pole. Women (sensibly, in my view as I have done the same) are more likely to think other things more important.

Thus, the brute fanny-counting of media analysis of sexism and the "gender pay gap" ignores female choices and attributes, thus denigrating both women and men for the choices they make. Women for their part see their contribution to society in caring professions such as medicine (more doctors are now women, as well as nurses) and teaching denigrated because these women aren't seeking to be at the top of BAE systems, or whatever. Likewise men, when they see women are going to hired so they form 50% of the workforce of a mining company feel devalued for their skills and attributes because the only way BHP Billiton could make 50% of its employees women is by discriminating against the larger number of men who will apply to drive a bloody great truck miles from nowhere in a bloody great hole in the ground in the middle of a bloody great desert surrounded by nothingness, and live in towns whose bars serve tinnies through wire grilles, and where kicking each others' heads in represents the primary saturday night entertainment.

But worse, by forcing women into traditionally working class men's jobs, you further alienate and disorientate a bit of society which already feels put upon, neglected, belittled and scorned. This is why they voted for Brexit in the UK, and in the USA, will vote for Trump. Working class men are lashing out, because their raison d'etre, to provide for their offspring, has been nationalised, and no other opportunity for them has been provided and they as individuals have too often been thrown on the scrap heap, derided as workshy deadbeats. The working class used to have pride in providing for their family and often doing dangerous, dirty jobs to do so. Opportunity isn't "equal access to university", for which working class men is a middle-class rite of passage, but decent jobs that will allow them to support their family, but which is blocked by the petty credentialism that values paper qualifications over experience and dumb diligence over inspiration.

That loss of pride is agonising. And people mourn loss far more than they celebrate gain. The aim of this post-modern obsession with equality of outcome therefore might as well be to make men despise themselves and women feel inadequate for the inclinations their biology and society has fitted them. Men become 2nd rate women, and women become 2nd rate men. By all means allow everyone to seek their own path, but to imagine men and women will sort 50/50 everywhere is totalitarian in its foolishness and cruelty.

The EU Deserves what's coming.

By Jackart (noreply@blogger.com)

One of the main reasons to oppose brexit is that the UK doesn't benefit from being "out" should the EU collapse. A disorderly break-up of the EU would damage the UK, independently of our status in or out. (any comment saying "it's better to bail early" will be deleted as a failure of comprehension read the post, please, it's that argument I'm dealing with). Indeed preventing a disorderly collapse should be the UK's priority. And when we were in, a disorderly collapse was unlikely. The UK kept the lid on Brussels insanity. Not only has Brexit given free rein to some of the very worst people in the UK, it also removes a brake on the insane Federasts  of Brussels.

Far from Remainers "talking the UK down", Brexiters have been doing so for decades - talking down the UK's influence in the EU to the extent we're actually thinking of walking out of the UK's proudest creation: the single market. It is now a shibboleth that the UK has "no influence in the EU", whereas the UK drove the single market, kept half the continent out of the poisonous grip of the Euro and pioneered enlargement to the east following the end of the cold war. The UK drove Russian sanctions to this day. The UK was one of the Big three and on many issues, more influential than France. The UK largely writes EU financial regulation for example (as is meet and proper).

But the EU over-reached. Voters, especially in the UK resented the EU's usurpation of the trappings of National sovereignty far more than the reality of "the laws made in Brussels" which was really just code for an underlying vision they (and I) don't like. And what is true of the UK is true of France and the Netherlands and everywhere else. Remainers like to mock the Be.Leaver's joy over the anticipated return of the blue passport. I however have long resented the words "European Union" above (ABOVE!) the crown on the front. It's like the bureaucrats are trying to rub the British People's nose in it. It's a symbol of something burning in the EU's core, which the average voter neither desires, nor trusts.

The ridiculous and unnecessary potemkin parliament with its farcical shuttle from Brussels to Strasbourg focusses the voters minds on the EU, without giving them any outlet to do anything about it. The EU looms much larger than it ought as a result of the charade of Euro elections. Democracy without a demos is pointless - what commonality do Socialist members from spain and the UK have?:

The EU was flawed, Thanks to the UK some of its worst excesses - the Euro for example were limited to countries that really wanted it. And now without a powerful country holding the reins and steering away from "ever closer union" the Brake that was put on at Maastrict and beyond will be removed. The EU will integrate itself to death, there will be chaos when the voters of Europe can take the tin-eared arrogance of Brussels no more. There was no need for all those millions of lives to be attenuated during that process. While leave voters will say "I told you so", a better analogy would be jumping out of a moving car suffering broken bones and extensive skin abrasions, but saying "it would have been worse" because the lunatic who grabbed the wheel when you bailed steered it directly into a tree.

Spending 1% of GDP to write trade and some business law could much more easily be done intragovernmentally, with a humble and small central bureaucracy. There is no need for "Presidents" and parliaments which lead to grandiose visions; visions which slam painfully, like the Euro, into the unyielding wall of reality. Unobtrusively aligning business regulation and deepening economic integration is necessary. A parliament, a flag, an anthem and a head of "state" are not. The EU has paid the price for this arrogant and pompous grandiosity.

Both the EU and UK are and will be significantly worse off as a result of Brexit. And now, just as Brexit is a bad idea that will be tested, so too will European integration. Both Brussels panjandrums and the brexiters fed off each others' fantasies. Both needed to believe integration was happening, even if it wasn't. Ultimately, the costs will become apparent to the UK pretty rapidly. The EU will suffer much more slowly. It's almost like co-operation is a non-zero-sum game, or something.

On Populism: What do we do? vs Who do we blame?

By Jackart (noreply@blogger.com)

If you ask the wrong question, the answers will not work.

"Populism" is, like pornography, hard to define, but you know it when you see it. Wikipedia defines it thus
"a political ideology that holds that virtuous citizens are mistreated by a small circle of elites, who can be overthrown if the people recognize the danger and work together. Populism depicts elites as trampling on the rights, values, and voice of the legitimate people"
It's clear Farage's lauding of a victory for "mediocre ordinary, decent scum people" he was speaking in this vein. But I don't think this captures the essence of populism. Mainstream politicians "Managerialists" in the Populists' vernacular ask "how do we solve this problem". You can be a capitalist, or a socialist, believing in different answers, but at least you agree on the question. Populists aren't asking this question, but instead "who do we blame?". The answer given by Momentum and UKIP may differ: Bosses vs Immigrants, Capitalists vs the EU but the question is the same.

There's also the populists view that MPs rather than being representatives paid to exercise judgement, are delegates paid to vote on someone else's behalf. In this, Paul Mason and Douglas Carswell are in agreement. But this is simply mob rule and behind it is a fear that legislators may Go Native, if they're allowed thanks to the corrosive influence of "[insert boogeyman]" in their long-running campaign to keep the "real" people down. But perhaps legislators know best; they have exposure and access to what passes for facts in this field, and are paid to study it, maybe there's something in the idea of representative democracy after all.

It's always easier to imagine you're the victim of an elite conspiracy, subject to "discrimination" on the grounds of class or race, or at risk from being "flooded" by immigrants, than it is to answer the question "what to I do?". Whether you're running your own life, or that of a nation, what to do is hard, and one of the stresses of modern life is the extent to which people are free, which means they have to make choices. No longer can you just follow dad into the Factory. Because many suffer from crippling loss aversion, these choices are scary, which is why stupid people yearn to be led. They look for leaders who offer answers which fit their prior prejudices and make sense of a complicated world. Corbyn and Farage have made careers finding and stroking a tribe's prejudices, soothing their people's indignation against a world they feel is against them.

The reason populism is so toxic to political discourse is that in apportioning blame, they create a slipway for the launching of vastly damaging ideas. "It's all the EU's fault" leads to Brexit*. "It's all the Fat Cats' fault" and you have a country that looks like Venezuela. If you start blaming immigrants or minorities, well we saw where that went in the last century. It's also why the Brexiteers ran from office at the moment of victory. Delivery isn't in the populists' skillset. The permanent masturbatory pleasures of opposition are what they crave, always losing so they can keep telling their people the game's rigged against them. If they win, then all those inadequate people will have to start making choices and they feel completely lost again. Much easier to simmer in resentment against an immovable object which allows you to blame it, rather than yourself for your failings.

*This isn't a place for a debate on the merits or otherwise of Brexit. Any comments on that subject will be deleted.

One of the reasons for the Populist's success (please note the "one of" at the start of this sentence) is Russia on the internet. The internet allows people to form much denser ideological defences against reality. And into the internet, there is a wounded superpower, pouring poison, poison which people use as ammunition in the defence of their ideological redoubt. Putin's toxic little propaganda swamps like RT and Sputnik are manufacturing and promoting stories which appeal to the populist mindset. Notice how Racists will share RT stories about Immigrants raping white women while members of the Green party will share horror stories about fracking from the same source. Some of these stories will be true. But many are manufactured, exaggerated and twisted specifically to support any party or idea that causes problems to the democratic governments of the west. This is not a random process. It is directed and controlled by the intelligence agency which has captured Russia. Maskirovka raised to a governing principle.

One of the reasons for the UK's relative success as a nation is that up until now, we have been mostly immune from the allure of the populist demagogue. We simply don't have it in us to put too much belief in one man, whether as protagonist or antagonist. Let's hope Brexit is a flash in the pan, and not part of a widespread descent of mature democracies into populist demagoguery. We'll know in 12 months whether democracy can survive or whether, thanks to Trump, Farage and Le Pen, we're going back to pogroms and a summer "campaigning season".

Please let's stop listening to Putin' useful idiots pedalling fallacious simplicity, and start listening to fallible and all-too-human experts again. At least the experts are asking the right question.

ESP32 progress

By noreply@blogger.com (RevK)

I am, of course, reinventing wheels, yet again with my move to ESP32 for IoT and access control stuff.

It is going well - I am actually on holiday in Greece, but whilst my wife and daughter sit on sun beds, I have been messing with code. Yes, I took a laptop, programming cable, some ESP32 boards, and even a hand held oscilloscope on holiday with me. Sorry. I spent 4 hours on the plane reading the data sheet on my iPad!

My plan is to get my ESP32 development environment and toolkit working like I had on the ESP8266, and build up my "Solar System" alarm and door control module code.

In some ways this is a nice project - it is often good to rework and redesign something for the second time as you have learned lessons and can do things better. It is also nice that my code effectively has a specification to work to. The previous code was being specified, designed, and coded all at once. But now I have a system which works with the ESP8266 modules and should work just the same with the ESP32 modules.

Progress

I am making good progress. I have the build environment on Mac and linux working. The instructions were very clear. I am actually ssh'd to my linux box to develop now. I have the basic support system which allows me to connect to the WiFi, and MQTT server, and do over the air s/w updates, and store settings in non volatile storage. These are all based on the ESP-IDF tools, which seem quite good.

I have hit a few snags - for example I cannot do certificate pinning on the TLS using the SHA1 hash of the certificate, it seems. I have to include the PEM format certificate itself. This seems needlessly complicated, but not a big change.

I have a snag with MQTT that it seems not to pass on messages with zero length data for some reason. I may have to work that out and do a pull request to the library.

I also have to restructure the design in some case - for example, previously I did the settings in a block of memory that was flashed. Now I am using the provided NVS library for settings. It seems good as it does not have to re-flash a block on every change - it incrementally updates a log of settings in flash as it goes. Quite sensible really. However, it seems to lack a means to enumerate the settings that are there - I have to ask for a setting by name. As a result I have changed my code structure to register settings from the application with my library. This has meant a redesign, but as always it makes it much better in the long run.

I should be able to sort the basic GPIO (inputs and outputs) really easily, and hence use almost all of my existing door control code with that. It looks like I can compile my linux version of DESFire stuff for NFC too, so no need for an ESP specific version (if needs ESP specific AES calls).

There will be some challenges with the PN532 library and VL53L0X library I expect. But they are not rocket science, thankfully. The Honeywell Galaxy RS485 bus should just work if I can work out the timer interrupt logic. Saldy each of these is simple in theory but getting my head around the specific way of working for the ESP32 takes an unknown amount of time. Interrupts were a challenge on the ESP8266!

PCB design

This had me kicking myself the night before we left on holiday. I have made up several ESP32 based PCBs. A general break out board; a door controller board; and several Galaxy keypad boards. I had checked I could load code on the door controller. It worked.

But I realised, on reading more of the data sheet, that 6 of the pins on the bottom of the module (ESP32-WROOM-32) are connected to the internal flash and so unusable. Why even have them come out of the module then? They are literally in the way of tracking the usable pins out of the chip. I had the same issue on the ESP-12F and taped over the pads. The ESP-12S sensibly does not have them, but then has a GND pad you have to tape over if you have tracks under it. The ESP32-WROOM-32 has both unusable pins on the end and a GND pad in the way. Arrrg! Even worse, it is only 6 pins of 10 at the end that you cannot use, so not actually easy to tape over just the 6 and still sensibly solder to others. I had read a web site on "safe" GPIO pins, but that documented the ESP32 chip not the ESP32-WROOM-32 module, and for that the pins 6-11 are "safe" to use as GPIO. There are other pins that are iffy - i.e. needs to be high or low at boot. Those I knew about.

The end result is that the keypad boards I made would simply not work! So I was left with three boards in total to take on holiday. The door controller would not "work" for the GPIO pins, but they are not connected to anything that would stop the chip actually working. So usable for developing code. I have managed to trash one board already with a flash / secure boot mix up!

The first thing I did when I got here was re-work my designs on the basis of taping all 10 connectors on the end of the chip. If I had not, it would have been bugging me all holiday. I had to be a bit creative in some cases, but I managed to redesign both boards. I'll mill them when I get back (I don't think my wife would have let me bring the milling machine on holiday for some reason).

Door controller PCB

Security

Security with any IoT stuff is important, and reading the data sheets on the ESP32 and using the build environment, I can see that Espressif have done their homework. Nothing is 100% secure, but they are really trying hard here - which is good news as a lot of IoT stuff is totally crap!

The chip has a secure boot mode. This means the first stage bootloader in ROM checks the signature on the bootloader in flash before running it. The second stage bootloader checks the signature on the application. Doing an OTA (over the air) s/w update also checks the signature, even. This means you cannot load wrong code on the chip!

It also supports encrypted flash. This means that I can have the code, and all my settings (like login details to the MQTT server, and WiFi) securely stored in the flash. Even pulling it off the board won't get you the data.

It has a set of eFuses, which are built in to the chip. They can only be "blown", so are bits you cannot unset once set. These include bits that stop you reading the eFuses, but they are wired internally in the chip hardware allowing it to encrypt and decrypt the flash. You can even set a fuse to disable the JTAG debug port. This means you can really lock it down so that you cannot get credentials out of the chip or the flash. It might not stop someone de-capping the chip package and using a microscope to read the fuses, but it will stop most conceivable attacks.

They have built in AES, SHA, BIGNUM (RSA) and TRNG hardware as well, and the API makes using https and TLS simple and encouraged.

In short, security seems to have been done very well, and thought about at the outset in the hardware and software design. This is almost unheard of in the IoT world, which is generally pretty shit at security. Well done Espressif.

Opportunities

Once I have this starting point - code and PCBs - and it is working, then (apart from updating my existing door controllers), I have to think of other uses for these chips. They are packed with stuff - included a dedicated separate ultra low power processor. The opportunities for very low power devices are interesting. Even just the bluetooth looks fun - I could make it that door entry using a key fob needs to be able to see my phone in range, for example. I am sure there are a lot of opportunities!

They have some interesting direct AP-less WiFi modes in the API as well, and some low rate long range (e.g. 1km) WiFi functions too. There is even a MESH WiFi system. This all makes for some very interesting possibilities to make systems that do not rely on infrastructure such as access points.

I still have not made my environmental monitoring device yet, so that may well be the next project after this.

Review

Controls are a little hard to figure out.

Conference Question

I also have an utterance. Less of an utterance and more of an incantation. Less of an incantation and more of a malediction. Less of a malediction and more of a Word of Power. Less of a Word of Power and more of an Unforgivable Curse.

Episode 85. Monitor the World with JMX!

There are technologies that sometimes are forgotten in a lonely corner, but that actually are quite sturdy. One of these is the All-Powerful Java Management Extensions (also known as JMX). With JMX you can actually expose a lot of metrics of your application and TONS of libraries use it "out of the box". Libraries like Tomcat, JVM, ActiveMQ, Spring (and ton others) exposes their metrics through JMX. And you can too!

In this episode we go over how to both consume JMX metrics (through JConsole, or statsD, or other Performance Monitoring Tools), and how to produce them as well (By creating your own MBeans), not only that, but we also go with how to be able to "invoke" these on a live application. Have you ever wanted to say "Oh my, I wish I could call this method while the program is running in production 'At will'". Well, with MBeans, you can make that happen! Not only that, but if you really want to you can also expose your MBeans through a Rest Endpoint with Jolokia.

FOLLOW US JavaPubHouse on twitter! Where we will be sharing new tech news, and tutorials!


We thank DataDogHQ for sponsoring this podcast episode

DataDog Logo

We also thank OverOps for sponsoring this podcast episode

 

OverOps Logo
Don't forget to SUBSCRIBE to our cool NewsCast! Java Off Heap



Do you like the episodes? Want more? Help us out! Buy us a beer!



And Follow us! @javapubhouse and @fguime and @bobpaulin

 

Serena Versus the Drones

After the test, she said that if she had a choice, she wouldn’t defend herself against drones using a tennis ball and racket, though she would absolutely pick them over other sports equipment. But, she added, "Drones don't bother me."

ESP-01, ESP-12F, ESP-12S, ESP32

By noreply@blogger.com (RevK)

As I mentioned, I am trying to move my IoT development stuff to ESP32, and notably move from Arduino IDE to the native Espressif ESP32-IDF using mostly C.

This poses a slight problem. The development environments are sufficiently different that it is something of a pain in the arse to maintain both. Probably just about possible, but not simple. So I'd like to basically abandon the ESP8266 / Arduino stuff (I'll leave on GitHub) and start working only on the ESP32 based stuff.

This poses a slight snag as one of the use cases for this IoT stuff is a very small device placed inside a Honywell Galaxy alarm system keypad.

This is based on an ESP-01 module which is ESP8266 based. It is not ideal as not CE marked, no RF can, very limited IO pins, but it is small. It comes with a 2x4 0.1" pitch header and is just the right size to fit a Pololu 3V3 regulator between it and the PCB, which is rather neat.

This use case has the ESP-01, a 3V3 regulator, an RS485 SO8 driver, and a small 2x2 milli-grid header for programming and debugging.

The whole thing fits in the small space inside the keypad, as per the picture on the right.

As a result I assumed I'd be stuck with, at the very least, my keypad code on the ESP8266 / Arduino based code.

The other IoT devices are generally based on either ESP-12F, or preferably ESP-12S modules. These are only slightly narrower than the ESP32-WROOM-32 modules I am now using, and so easy enough to make alternative designs using the ESP32.

The ESP32, however, whilst larger, has a lot of IO pins. It is possible to track these pins so that you have quite a few usable IO pins, and GND and 3V3, all on one end of the chip if you try :-


I have actually tracked Tx/Rx programming pins, and GND and 3V3 "through" some unused GPIO pins, which allows its all to come out of the bottom end and keep the whole thing quite narrow. Obviously this is fine as long as I don't make those pins an output :-)

Once again, the new milling machine is doing a great job, and I was able to solder it.


And, to my delight, it fits, and the keyboard PCB fits over it with no problems!


So, yes, I can actually move away from ESP8266 now. I have to migrate and re-work the various tools and modules I have made, but once done, I can continue development on ESP32 only. Yay!

P.S. Always tweaking :-)


Moving to ESP32

By noreply@blogger.com (RevK)

Someone already told me that once I move to ESP32 I won't want to go back to ESP8266.

Well, apart from the fact the ESP8266 is available in a tiny ESP-01 package, I think they may be right. The ESP32 in a WROOM-32 package is slightly wider than the ESP12 package, but because the GPIO pins can be mapped any way you want, pretty much, the tracking takes less space. So overall I was able to make an equivalent board in less space using ESP32.

The big challenge for me with ESP32 was the concern over the fine pitch - 0.9mm contacts at 1.27mm spacing. I was not sure I could either mill a PCB or solder a board at that pitch reliably.

I was wrong, I can!

New door controller using ESP32

Fixing the X axis backlash on my mill was a big help as well. So yes, I can use the ESP32 modules.

The other concern was the price, but it seems that getting a proper ESP32 is as cheap, if not cheaper, than an ESP12S. Yes, some cheap ESP12S can be got, without CE marks, and missing FCC ID, and not quite working well on WiFi. But £2.91 for ESP32-WROOM-32 from a reputable supplier is pretty good.

There are advantages to the ESP32, obviously, including much more RAM, faster, dual processor, more GPIO, more UARTs, and so on. It is a good comprehensive IoT device.

The other big change I want to make is moving away from Arduino IDE. Yes, Arduino makes things easy, but it turns out the Espressif IDF for ESP32 is pretty good. It has the key things like https, MQTT, OTA, and all sorts as standard.

So my plan is to make an ESP32 IDF based version of my door controller, including PN532, VL53L0X, etc. This should not be too hard, and a lot of my existing code can easily be ported to a simple C code environment.

I am, however, impressed with the documentation - not only of the APIs in the IDF, but also the technical reference manual. It actually details all of the registers clearly.

The other impressive thing is Espressif seem to to be trying to encourage some best practice in IoT. They have secure boot and signed images as a standard feature. Even https libraries, as standard. It means that making a reasonably secure IoT device is easy. Well done.

I managed to get the build environment working on Mac and linux with no problems, following the clear instructions. I built the "hello world" example, and flashed it with no problem.

I am off on a 4 hour flight shortly, so I have the 669 page PDF of the technical reference manual on an iPad for some light reading on the flight.

Of course, once I have this off the ground, I can look at Bluetooth, and CAN bus, and all sorts of new fun things.

Old Game Worlds

Ok, how many coins for a cinnamon roll?

New milling machine

By noreply@blogger.com (RevK)

I had a CNC 3018 PRO machine from Amazon.
So, especially given the bent screw, I decided to get a replacement, and went for a CNC 3020 from Amazon.
  • This is a much more "industrial" unit - proper drag chains and cables. Looks good but costs a bit more.
  • Work area (you guessed it) 30x20cm
  • Comes pre-built
  • Has a boxed controller and power supply, with nice connectors to the machine.
However, it has a challenge or two - the main one is that it works with some very specific software (a pirate copy of which is apparently supplied with it). It has parallel (!) and USB, but the USB only works with that software and only on a windows machine - it does not even appear on a Mac even in a debug log! The software did not look too good anyway! This is a bit of a bugger.

The upgrade

My solution was to upgrade the controller to a TingG controller. This adds a bit to the cost (especially with the customs/VAT and admin charge from US). But was not actually at all hard to do.

The old controller is bolted to a heat sink, and can simply be unscrewed.

The new controller is slightly smaller. I needed to drill/tap an M3 hole in the heat sink for one of the screws so that I could screw it to the heat sink. I also cut an extra hole in the case for the USB connector.

Also, I decided to use the heat sink, which meant something between the new controller board and the heatsink. I got some small copper block / heat sinks and thermal tape - and I used them between the back of the new board and the existing heatsink.

The TinyG is designed to work without the need for a heatsink, but this can not really do any harm.


The connectors on the old board were not the same, though the new board has pads which means I could have changed them. I decided instead to use the screw terminals on the new board, fitting bootlace crimps on the wires. I had to swap the cables to make them fit, and use the motors 2, 3 and 4 as closest to edge of the case. But they all fit!


I also connected the emergency stop to the reset pins. I have not worked out how to connect the spindle drive to the new board yet - that is a challenge for another time. The case has a button and a dial for that which works for now.

I also got a 6 way (plus ground) chassis DIN connector and wired up all 6 end stops (X/Y/Z Min/Max).


This meant various micro switches, and super glue on the actual machine.


And also, the Z-Min I wired to croc clips to allow me to use contact to PCB for homing Z axis.

The TinyG has good documentation, covering setting the current, etc. I was able to set the axis to the right motors, and set the polarity and travel per rotation, etc.

However, the bCNC code I was using did not like the TinyG, and I ended up installing CNC.js which works nicely on my Mac. It does lack the multiple point Z axis levelling of bCNC sadly, but works.

I did have to configure the homing for Z axis a tad as it is designed for a switch from which you back off. For PCB contact you get the exact Z home, and do not need offsets. But this is all well documented.

Slack (backlash)

However, even though it is a really good solid construction and seems to have no play, I found there was an issue with the X axis. It seems to have around 0.1mm slack on it. This means if you move right to a point it is actually around 0.05mm short of it, same if moving left (i.e. is then 0.05mm right of it). Make a row of left and right moves and lines and you see the problem clearly. This test shows it well - centre is left and right moves before each line, but left is all moves from the right (apart from first, bottom left) and right is all moves from the left. Repeatable slack!

Oddly the Y axis is absolutely spot on!

This is not really enough of a problem to cause issues making PCBs, but is annoying so I wanted to fix it.

I checked for anything loose, and also checked the TinyG for any options, but to no avail.

My solution (now updated to the eps2gcode tools) is software to compensate for slack. This seems to work.

Overall I am quite happy with the result.


P.S. Tightening this nut a bit fixed it.



P.P.S. After reading the TinyG docs I am actually running at 4 micro steps not 8, so 1/200th mm spacing, which I think will be more than adequate for anything I am milling. I suspect 1/100th mm would be fine but actually that is a lot more noisy. Now that the backlash is sorted maybe I'll go back to 8.

Keycloak and JDBC Ping

By Sebastian Łaskaiwec

A few months back, we had a great article about clustering using JDBC_PING protocol. Since then, we introduced some improvements for the Keycloak container image that can simplify the setup. So, before diving into this blog post, I highly encourage you to visit the Keycloak Cluster Setup article.

What has changed in our Container Image?

Probably the most important change is configuring the JGroups discovery protocol by using variables (see the Pull Request). Once the change got in, we could configure the JGroups discovery by setting two properties:

Let's apply the changes, shall we...

The JDBC_PING-based setup works fine in all scenarios, where we connect all Keyclaok instances to the same database. Since JDBC_PING can be configured to obtain a database connection using JNDI binding, it can easily connect to the Keycloak database. All we need to do is to add two parameters to our docker image:

You may find an end-to-end scenario here.

Additional configuration

In some scenarios, you may need additional configuration. All additional settings might be added to the JGROUPS_DISCOVERY_PROPERTIES. Here are some hints and common problems, that you may find:

Problem description Possible solution
The initialization SQL needs to be adjusted In this case, you might want to look at initialize_sql JDBC_PING property
When Keycloak crashes, the database is not cleared Turn remove_old_coords_on_view_change property on
When Keycloak crashes, the database is not cleared Also, when a cluster is not too large, you may turn the remove_all_data_on_view_change property on
Sometimes, Keycloak doesn't write its data into the database You may lower the info_writer_sleep_time and info_writer_max_writes_after_view property values

Haven fun and don't forget to let us know what you think about this blog post using the User Mailing List. Sebastian Łaskawiec and the Keycloak Team

Swap, swap, swap, and bad places to work

It's been an interesting couple of days in the world of Linux VMs.

Earlier this week, a post to the linux-kernel mailing list talking about what happens when Linux is low on memory and doesn't have swap started making the rounds. As the author points out, things get... interesting.

I saw this, and started thinking of my own experiences in this realm, and then a second post appeared on the same topic, citing the first, and adding more color to the picture.

And then, someone submitted an eight year old post of mine to HN this afternoon, and that brought the whole thing back around.

So, yeah, back in 2007 at some big web company, the hosts started paging me, and I'd log in and they just ... felt weird. They felt like something I couldn't quite put my finger on at the time, but it was probably something I had picked up back in the '90s when messing with much smaller Linux boxes. Maybe I tried running without swap and paid the price. It's been so long I'm not even sure any more.

Then, closer to 2016 or so, I hit it again at some other big web company. There, I was trying to advocate for "tinyswap" -- not NO swap, but not massive multi-GB partitions, either. I met one of the memcg developers at the time who also thought it was a good idea. Sadly, I never saw any progress on the technique after moving teams and before leaving the company a few years later.

Now, here we are in 2019, and we have a fresh set of people still fighting over it, like it's some kind of brand new dilemma. It's not.

I stand by my original position: have some swap. Not a lot. Just a little. Linux boxes just plain act weirdly without it.

This is not permission to beat your machine silly in terms of memory allocation, either. I'll just steal something that I had queued up for another one of my "reliability list" posts, but which is too useful to leave out of this one.

Item: If you allocate all of the RAM on the machine, you have screwed the kernel out of buffer cache it sorely needs. Back off.

Put another way, disk I/O that isn't brutally slow costs memory. Network I/O costs memory. All kinds of stuff costs memory. It's not JUST the RSS of your process. Other stuff you do needs space to operate. If you try to fill a 2 GB box with 2 GB of data, something's going to have a bad day! You have to leave room for the actual system to run or it's going to grind to a stop.

So, yes, the original team I landed on had done a bad thing by trying to pack the machines to the gills with data. But, what was more likely to happen on a team where they didn't take me seriously? Me getting them to "be less efficient" in terms of memory utilization (while raising the reliability factor by not having it constantly choke on replication), or me bringing back swap to let it keep going the way it had been?

Would it change your mind if I said they were also doing the classic $thiscompany thing of "this one is deprecated and the new one (which isn't ready yet) will fix it"? Would you still think they'd take me seriously and "spend resources" on re-sharding everything to not use so much memory?

How about if I added this was the same developer who rejected a patch I made to fix another problem where it would barf on db files greater than 2.1 GB because their scp binary wasn't compiled with large file support?

Imagine it: it's your first time at a megacorp in Silicon Valley, you're already not sure you belong there at all, you're more than a little homesick, and to top it all off, tons of people are acting like complete assholes and hating on your attempts to just make things run properly. Could you really thrive in that environment? What makes you so sure?

Oh, you must be THE ONE.

For everyone else, you'd probably cry too. I sure did.

I don't work there any more. Obviously. And I'm happier for it.

Any lazy fool can deny a request and get you to "no". It takes actual effort to appreciate and recognize what they're trying to accomplish and try to help them get to a different "yes".

Body cams are weird #GDPR

By noreply@blogger.com (RevK)

Body cams are weird - you walk in to a shop with one on, especially with a big WARNING: VIDEO/AUDIO RECORDING sign on it, and you get strange looks. But at the same time, half a dozen CCTV pointing at you, is "normal".

I have yet to be challenged on this, and I am not sure I really want the altercation, but I imagine if asked to "turn that off" it would be valid to say "I will, if you turn that off [point to CCTV]".

Let me stress, I am not a lawyer or GDPR expert, but I'll try and make sure this post is technically accurate and update any errors pointed out to me. And thank you Neil for the links, case references, and definitions and correction of my many typos.

Domestic purposes

One of the things that is outside the scope of GDPR is processing of personal data by a natural person in the course of a “purely personal or household activity”, which is commonly called processing “for purely domestic purposes" This is a tad complicated in interpretation though, and there is some confusion on this.

There has been a judgement on fixed position CCTV cameras recording in a continuous loop in people's homes (domiciles!) that says that it is only domestic purposes if there is no view of any area outside the property boundary. i.e. if the CTTV covers the road, or neighbour's property, it is not domestic purposes and hence you need a lawful basis, a privacy notice, to comply with data subject rights, and everything else that goes with GDPR compliance.

Personally (and I think I am not alone on this), I think this ruling is wrong: The cost seems to have confused the setting of the processing with the purpose of the processing. The classic example of domestic purposes, an "address book", will have names and addresses and phone numbers of people not within the boundary of my property. So clearly it is not the data subjects that define "domestic purposes" but the "purpose" of the processing (the clue is in the words used!). But as we have a ruling, that becomes risky to try and argue that CCTV covering the road is domestic purposes.

Indeed, for security of my home (domestic purposes) I want recordings of people "casing the place" (on the road or opposite my home) as that may have much better, daylight, views of faces to use when later the same vehicle is caught on camera relating to the place being burgled.

Dash cams, and helmet cams, were at that point considered "OK" though (or, at least, there has been no case law yet involving them), which leads to a whole series of scenarios where one cycles home and puts the helmet cam on the wall of the house, and then connects to power, and so on - at what point does it change from "OK" as a helmet cam covering the road, to not "OK" as a CCTV covering the road?

So, to be clear, if you have seen my body cam and found this post because the URL on the label, my purpose in recording is domestic purposes - I record my cycle rides mainly. Just like a dash cam in a car.

Continuous recording

There is some draft guidance on the matter of dash cams which actually suggests that dash cams must not  be continuously recording!

Yes, you read that right - it seems to suggest that you have to start recording just before the "accident" you want to record. I know, that is utter madness.

Of course there are plenty of people who will jump in and point out the technical solution is a pre-recording buffer. That is only saved if you "press the button" (after the accident), or the camera detects a collision, even, which some can!

But that *IS* processing personal information, even if it just goes in to RAM and is replaced after 5 minutes. An example of why that is processing is simple - imagine a dash cam where the camera owner is the cause of an accident. They have a choice about processing of that personal data in the RAM of their camera - they can choose to "press the button" and save the (incriminating) evidence, or not. The data subject can, if they are quick, ask for a copy of that data, at which point they have to "press the button" as otherwise they are deleting personal data after being given (what used to be called) a subject access request. Clearly the data in the pre-record buffer is personal data, and there are data processing decisions to be made in relation to that data.

We can only hope that such a nonsense paragraph is removed from this draft guidance (or, even better, replaced with something more akin to common sense, that someone trying to protect themselves from an accident, or put themselves in a good position if they do suffer an accident, is “domestic purposes”, even if it films someone else in a public setting).

Transition from domestic purposes to commercial

There is then one area about which I am really unsure. If I have personal data, recorded purely for domestic purposes, e.g. my address book, but I then tweet that!

In another recent case, the Court of Justice of the European Union has held that "since [someone] published the video in question on a video website on which users can send, watch and share videos, without restricting access to that video, thereby permitting access to personal data to an indefinite number of people, the processing of personal data at issue in the main proceedings does not come within the context of purely personal or household activities”.

Applying this to a dashcam or helmet cam, even if my filming would be considered to be “domestic purposes”, posting the resulting video on Twitter or Facebook (e.g. to highlight bad driving, or just as part of a journal of my day), unless I lock down access to the video, it looks like I would be outside the scope of domestic purposes.

Given how many people live out their lives online, engaging in the type of chat that one might previously have had in a pub or coffee shop, this feels like it could entail the imposition of GDPR — which, let’s face it, is not something normal people should have to understand — on the general public, for very common activities. It seems to me like rather too much of an interference with people's private lives.

Somewhere in between

Sharing a family video (e.g. my grandson riding a bike for the first time) with family is clearly domestic purpose.

I assume doing so over iMessage is still so, as I do not hand the video to Apple (end to end encryption).

What of posting to a small (family) group on Facebook? My "purpose" is clearly domestic, but Facebook have the image and they do things with images that are not domestic purposes.

What does that mean for GDPR I wonder? I assume that this means that Facebook needs to comply with GDPR, but I do not (since I am not sharing with an indefinite number of people).

My take on a BSD/386 filesystem's contents in 1995

While grepping around some of the dustier corners of my home directory earlier this evening, I turned up some documentation for the "first real Unix box" I ever ran for other people (after inheriting it from an earlier admin). That is, this was a machine that had Actual Users on it who were using it to (try to) accomplish stuff - run gopher, telnet out to a MUD, that sort of thing. (Hey, it was quite a while ago).

It's interesting to see what my understanding of a BSD/386 filesystem looked like back then. This brings up a new kind of navel gazing I hadn't thought of before: reading your own documentation a few decades later to see if you've learned anything since then.

This is something I was asked to write up by the teacher who was nominally in charge of the box at my high school. I was the one actually running it (said so right on the "course" description), but they needed an actual teacher to keep an eye on things. He asked me to explain what was on the disk in broad strokes, and this document is the result.

The date on this is August 28, 1995. That I found it in the same month all these years later is just a nice 1-in-12 coincidence. Don't read too much into it.

This was originally a plain text file with just spaces for indentation. Since that would fall apart really badly here in the HTML world, I've converted it to a series of nested ULs for your consumption.

I'm pretty sure a lot of this is/was just me propagating various incorrect notions, but let's just see how bad it was, really. Navel gazing mode: ON FULL BLAST.

And now, some extremely freeform reactions based on reading this for probably the first time in 24 years:

I'm pleased that I was reminding people that root should not log in even back then. Today, it might involve the word "evil".

I guess this is before I had been sufficiently chastised by the then up-and-coming Linux weenie crew for calling it "X-windows" and switched over to either "the X Window System" (no S!), X11, or just "X". Why the drama? Because "Windows" was the devil!!11! It was the 90s, what can I say?

Speaking of Windows, that date of this file looked familiar, so I went and looked. Sure enough, it's four days after the release of Windows 95. Start me up, indeed. (Did they ever listen to the other verses of that Rolling Stones song? You're doing what to a dead man?)

I'm pretty sure I didn't count myself in the set of C programmers back then based on the comments for /usr/include and /usr/lib.

/usr/demo? Emulating DOS? I sure hope I never actually tried that on that box. It had enough to do as a mere 486DX2-66 already.

Apparently the home directories weren't on their own device initially, hence /usr/home. Then, when they got their own drive, that was mounted on /home and /usr/home became a symlink to it. This happened before I took over the box.

I had a friend back then who loved labeling things 'sassy', kind of equivalent to "awesome" or "badass" or "lit" or whatever else we use today. They were namechecked in the original document but are parameterized here for their privacy.

This file suffers from the problem of "I hope you know how to look up some of this crap" when it says things like "fonts for the 'groff' program". If the reader doesn't know what groff is, and has no idea how to use man/whatis/apropos or similar, they're not going to learn much from this thing. They might intuit that it has something to do with fonts, but what?

I apparently had no idea what locales were used for back then, and thought cron was "wonderful". Ignorant bliss!

I hung a lantern on the whole love it, hate it thing with Sendmail. Just think of how many remote root exploits came through that giant swinging barn door in the years after this was written. Where are the time travel scifi novels about going back in time and rooting every single machine you see because you know all of the holes that won't be found for years or even decades? "Oooh, it's only 1987 so the Morris worm hasn't happened yet! Hello WIZ and fingerd overflow!"

As for "files included in a BSDI distribution", ermmm, isn't that damn near everything on the disk except for the stuff the users created? It's a poor description for /usr/src at any rate since it glosses over the whole notion of source code entirely.

My first reaction to /usr/ucb was a "wow, that's old". My second reaction was something vague about improv. Yes, and...

Did you notice that it's /usr/var on this box as the canonical location? I had to double-check the document to be sure when I hit that part of it. Crazy. Sure enough, down at the bottom, there's /var, pointing over at /usr/var. I guess the notion of "try to set up your /usr so it could theoretically be mounted read-only" never entered the picture. Nobody tried to do "common RO /usr over NFS" here, clearly.

Kernel hackers would care about what's in /usr/var/db? What?

Regarding /usr/var/emacs, technically the participants are the (human) users of the various editors. The editors themselves are just standing there watching people do their performance art. Funny, though, that there's no "vi" path anywhere, right? (Save your venom -- I used pico back then, and I still use nano today, including this right now).

I think /usr/var/games died because "we don't want students playing games on the school equipment". You know, because all of those ASCII orcs and kobolds are taking up SO much memory and CPU time. Of course, the older, more sensible me says "it's so the kids focus on their school work instead of leveling up in dungeons".

We had some user mailboxes which were "quite large" by my own admission, and right next door, we had nothing configured in /var/quotas. What an amazing coincidence!

Usenet, "presently unused". Did I think we were going to get newsgroups flowing in, whether via UUCP or NNTP? On a high school machine? If the powers that be didn't like nethack and rogue, what would they think of alt.2600 and worse? Yeah, that was never going to happen.

Possibly timely items from my reliability list

Last month, I put forth some items from my "reliability list". It's just a big list of things that bounce around in my head and tend to emerge when I'm troubleshooting something that broke, or while assessing a design for something new.

This time, I have some more. You may notice a pattern in the topics in this post. It's not a coincidence.

Item: Some days have 86400 seconds, while others have 86401, and some day we might have one with only 86399.

Leap seconds are a thing, and so days can change lengths in systems that many humans use for civil timekeeping. The planet's rotation has been slowing down which has been giving us positive leap seconds for a few decades, but who's to say it might not go the other way some day?

If this actually affects you, I hope you're getting paid well to deal with it. This stuff is a real pain.

Leap second with and without smear

Item: You get at most six months warning for days of nonstandard lengths.

Yep, a leap second at the end of June is announced early in January. A December event is announced early in July. In other words, even the inconsistencies... are inconsistent. You don't get any more warning than that.

Item: Some days have 1 AM twice, while some days skip over 2 AM entirely.

If your systems schedule things using time zone definitions which include daylight saving time, you're going to have fun twice a year. One of your days will go from 1:59:59 to 1:00:00, thus repeating the hour, and another day will skip from 1:59:59 to 3:00:00, thus skipping the hour.

If you have scheduled jobs in the 1 AM block, one day a year, they will run twice on a scheduler which does not account for this. Likewise, if those jobs are in the 2 AM block, one day a year, they will not run at all.

This seems obvious, but everybody gets this wrong eventually. If your company hasn't been bitten by this yet, wait. It'll find you.

Item: Not everyone goes on summer time, and those who do don't all shift at the same points in the year.

Some places laugh at DST and stay put year-round. Others shift. Still others shift, but do it earlier or later than the last group. This means that the offset between far-flung offices in your company might not be constant! Most of the year, it might be 8 hours, but for a week or two at either end, it might be 7 or 9. Think about the implications when scheduling meetings, or setting up on-call schedules which need to not have gaps in them.

Working on this kind of stuff will make you appreciate the meaning of "picking the wrong week to quit sniffing glue".

Item: Summer time rules have changed multiple times over the years, and are sure to change again.

The US switched as recently as 2007. Remember that?

Just wait -- it'll happen again, and a whole new generation of consumer electronics will start behaving weirdly for a few weeks every six months.

Item: US/Pacific is more insidious than anyone realizes due to Silicon Valley.

You might be surprised at how many Linux boxes are running US/Pacific instead of UTC because "it's too late to fix it now". I'm talking multiple millions here across many different companies.

Why? Because of all of the companies which started on the west coast of the US and used their local time on the servers.

I mean, sure, deep down inside, the kernel is doing its thing with the same time base, but odds are, all of those userspaces think they're in PDT right now.

Item: Not all time zones are at integer offsets from UTC.

Half an hour? Fifteen or forty-five minutes? Sure, why not. Those are the EASY ones. Dig around in the historical data for the really fun stuff.

Item: At least one month changes sizes periodically.

That's February, in case you hadn't noticed yet.

Item: Some programs written from January to September will break the first day of October.

Some people's tests really don't like two-digit numbers, what can I say? You think this is an exaggeration, but it's not. This actually happened to me, when someone who should have known better left such a timebomb behind in our continuous build test suite.

Item: Some programs written in October, November or December will break the first day of January.

Same idea, only inverted. Expect it to be two digits wide, and then suddenly it isn't? Welcome to the land of misfit software.

Item: Every four years, some programs break on December 31st.

This is because they didn't account for the year having 366 days. Any Zune owners still out there after all these years?

Item: Some programs broke September 8-9, 2001 (depending on your time zone), and not because they were trying to do some kind of Nostradamus thing for the coming week.

This bit someone in my family. KDE's "Kmail" broke because Unix time went from 999999999 to 1000000000. Yes, the text representation got wider and so it couldn't parse its own data files, or something like that.

I can't really prove it now, but I have a feeling that at least a couple of systems out there went mildly nutty in June 2004 when Unix time passed 2^30. Of course, the real fun for Unix time is still to come when we get to 2^31. See you in 2038.

Some items from my "reliability list"

It should not be surprising that patterns start to emerge after you've dealt with enough failures in a given domain. I've had an informal list bouncing around inside my head for years. Now and then, something new to me will pop up, and that'll mesh up with some other recollections, and sometimes that yields another entry.

One day I took a whack at getting it into text form. The items that can apply outside of a given company might be interesting. As for the ones that keep coming up, well, I guess you can join me in facepalming a little when it happens again.

I'll list some of them here and some of the thinking behind them. Just about everything here has happened at some point in time, and probably has happened more than once... way more than once.

Item: Rollbacks need to be possible

This one sounds simple until you realize someone's violated it. It means, in short: if you're on version 20, and then start pushing version 21, and for some reason can't go back to version 20, you've failed. You took some shortcut, or forgot about going from A to AB to B, or did break-before-make, or any other number of things.

It doesn't really matter HOW it was violated, just that it was. If you keep the general premise of "how would we roll back" in mind when designing and reviewing something, perhaps it would not happen so often.

Item: New states (enums) need to be forward compatible

Rollback compatibility isn't just about the code. What about the data emitted by something? Let's say you have a complicated state machine, and you add another state in version 21. It starts writing out records using that state, and that state didn't even exist before. If any version 20 instances ingest it, they're going to blow up.

So guess what happens if you try to roll back to 20? That's right, you quickly find out you can't. This puts you between a rock and a hard place in terms of dealing with a disaster if 21 should fail. If something bad happens, you now have to come up with a "fix forward" approach and hope that nothing else wrong is baked into that code.

Otherwise, you get to play "hand hack the state storage to fix the records that got ahead of what version 20 can handle".

This is kind of another A, AB, B, type of thing, and it crosses paths with Postel's Law. In this case, you need to make sure you can recognize the new value and not explode just from seeing it, then get that shipped everywhere. Then you have to be able to do something reasonable when it occurs, and get that shipped everywhere. Finally, you can flip whatever flag lets you start actually emitting that new value and see what happens.

Item: more than one person should be able to ship a given binary.

Hopefully this one gets people to stop and think "what do you mean, only one person, that would mean..." and then their thoughts spiral off into bad places and hopefully they start seeing the weak spots in their organizations.

This one is pretty straightforward: if only one person can ship a binary, what happens when they're not available? You get to work backwards on this one, too. How did you get into this state? This one is fun because it's both a tech question and a management question. Neither the coders nor the managers should have let this happen.

Hopefully you find this one out before you run into a situation where a critical bug fix sat in trunk for months and took down your business because someone was on vacation and the fix never went out.

Item: using weak or ambiguous formats for storage will get us in trouble.

Translation: if you aren't using something with a solid schema that handles field names and types and gives you some way to find out when it's not even there, you're going to be in a heap of trouble. It might look fine initially, but once time has passed and a few more programmers have rotated through, you'll be in a world of hurt.

Protobuf, Thrift, whatever, just use something like that. And, that leads me into the next one...

Item: there is far, far too much JSON at $company.

In the original, "$company" was the actual company name where I first put this into writing. The problem is, this holds true in far more places than that. JSON is not what you want when you're blasting data around and web browsers are not involved at either end. Service A talks to service B? Great! See the previous item, and use one of those.

On the other hand, if you only need 53 bits of your 64 bit numbers, and enjoy blowing CPU on ridiculously inefficient marshaling and unmarshaling steps, hey, it's your funeral.

Item: if one of our systems emits something, and another one of our systems can't consume it, that's an error, even if the consumer is running on someone's cell phone far far away.

Again, you might guess which company this came from, but it holds true everywhere else, too. If you wrote the server, and it sends out some data, and you wrote the client, and that client can't ingest it, that's a problem! It's not suddenly OK because the client is actually running on some random person's device out in the world.

Sure, some tiny quantity of users are the people who are actively fuzzing your client, trying to see what features are in it that haven't shipped yet, but everyone else is just trying to USE it normally. If you throw out a blanket decree to not investigate these things just because someone might be fuzzing it, that's just lazy.

Incidentally, this applies even more forcefully between internal services. If two parts of the company are talking to each other, and there's anything but a success for that request, something's wrong. A "HTTP 400 bad request" is only the sort of thing you get to ignore when it's coming from the great unwashed masses who are lobbing random crazy crap at you, trying to break you. Internally, that's something entirely different.

(Also, if you are literally having HTTP 400s internally, why aren't you using some kind of actual RPC mechanism? Do you like pain?)

That's enough for now.


August 1, 2019: This post has an update.

Travel Equipment: 2019 Edition

Taking a look at some of the key travelling equipment I've grown to like in the last year of travel.

ASGI 3.0

Upgrading the ASGI spec to simplify it, while keeping backwards compatibility.

A Django Async Roadmap

Taking a look at what it would take to make Django async-native, what it enables, and if we should even do it at all.

Python & Async Simplified

Event loops, coroutines and awaits, oh my!

Channels 2.0

Finally, the promised land is here and Channels 2.0 is released. But how much has changed? And why?

The Sheets Of San Francisco

Finally, my 3D city maps return, and this time they're mapping the streets and hills of San Francisco.

Channels 2: October

What's happening in the world of Channels? How many times have I rewritten the API now?

New Zealand

Adventures in the land of volcanoes, unique birds, fjords, and long white clouds.

Towards Channels 2.0

Outlining my plan for iterating on Channels' design, and what the future might hold for both Django and Python in general.

Taiwan

An adventure of food, cities, industry, and astonishing natural landscapes.