Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • The Awareness of Method

    February and March were weird for me. A lot of personal drama, and none of it really mattered to the masses so I kept it to myself and my close friends. I don’t feel the need to publicly broadcast my personal pain on everyone, and I do my best to step back and let it (hopefully) not impact my reactions to everyone else.

    This was very hard because a goodly portion of my drama was from the public sector, and it boiled down to people unintentionally hurting me. And as I grumbled on Twitter, I feel like I need to explain how having hurt feelings doesn’t mean I’m over reacting. Which is preposterous.

    I understand that, for the most part, the people who hurt me certainly did not intend to attack me or sound combative. And I’m well aware that tone is a terribly difficult thing to read in the written word. That’s why good authors take the time to explain things in detail. Things like italics and bold and capital letters are important for reading into the meaning of a sentence.

    At the same time, any time a comment aimed towards me starts with a remark about how they don’t care for drama, I walk away. You should never say that. If you don’t want to talk about the drama, don’t invite the drama. It’s really that simple. And if you’re worried that how you’re saying something, it’s a sign that you should rethink what you’re saying and how you’re saying. This is where the method comes in to play.

    The intent of what you’re saying is subject to the manner in which you say it. If you ask a sincere question and people react strongly and negatively to it, then your intent was lost in the method. Communication in text-only is complicated. You can’t see people’s faces, you can’t hear their tone, and most of us don’t know each other to the degree that we can reliably read intent. Simply put, your intent is subject to how it’s read.

    For a long time, I’ve advocated people remember that when someone misinterprets what they’ve said, the fault lies in both parties. If I say something and it’s read as aggressive, this is in part my fault for not tempering my tendency to be direct with the need people have for humanity in a conversation. At the same time, no matter how nicely I say “Your plugin has been closed…” someone is rightly going to read it and be angry and interpret that I am being mean or offensive.

    It’s a no-win situation. Or at least it’s one I’ve never figured out how to win. I’ve been told the default ‘your plugin has been rejected…’ email is too angry because it uses all caps for one line, even though it apologizes and explains it’s trying to get the reader’s attention and encourage them to … well … read. That email was developed over years of communications with thousands of developers. It’s the one we determined to have the highest success rate of people actually reading and processing what was said.

    Still, at least once a day someone replies to an email asking ‘How do I resubmit my plugin?’ This invariably comes in reply to an email that says “When you’ve corrected your code, reply to this email with the updated code attached as a zip, or provide a link to the new code for us to review.” And at that point, I honestly don’t know how to make it more clear.

    When people say the email is too aggressive, I explain that we’ve cultivated them over years, but we’re always willing and welcome to make it less so. And we ask if they have suggestions? Not a single person has ever replied with advice, except the person who said “Don’t use emoticons, they’re unprofessional.”

    Seriously, you just can’t win.

    Which brings me to the point and it’s that winning isn’t the point. Losing is the point. We lose when we don’t take into consideration the reaction to what we say. We lose when we dismiss someone else’s reaction. We lose when we over-react to what we perceive as an over-reaction.

    We will never be able to always speak clearly and without accidental misunderstandings. We will never be able to ask every question in a way that makes everyone feel welcome to join a dialogue.

    We can be aware that our words have weight and meaning, especially because an increasing number of us communicate in text first (if not text only). We can try to learn from our mistakes. We can apologize sincerely for those mistakes.

    What you say can and will be taken out of context. It can and will be read the wrong way. When it happens, it hurts and it tends to make you react poorly. But being hurt by someone’s words doesn’t mean you’re over-reacting. And it would do us all good to remember to respect other people’s feelings and reactions.

    Yes. Even me.

  • Making Plugins Filterable

    Making Plugins Filterable

    I’m really bad at thsi since, generally, I don’t know why people would want to with the plugins I make. Which means I don’t do it. Which means I didn’t know how.

    I have a plugin that records the IP address of users as they register and outputs it on the ‘show users’ page. It’s simple and it works.

    Someone asked me if I could make it link to a place where he could see where the IP was from. Now my intent with the plugin was to list the IPs so I could spot serial sock puppets. But this use-case, I agreed, was valid. I just didn’t want to tie my plugin into one service or another. So I made it filterable.

    As it happens, it was incredibly simple. This is filed under “Stuff I should have known years ago…”

    Make The Output Filterable

    Originally I had the code outputting $theip and, in order to make it filterable, I wrapped that with this:

    if ( has_filter('ripm_show_ip') ) {
        $theip = apply_filters('ripm_show_ip', $theip);
    }
    

    The whole function looks like this:

    	public function columns($value, $column_name, $user_id) {
            if ( $column_name == 'signup_ip' ) {
                $ip = get_user_meta($user_id, 'signup_ip', true);
                if ($ip != ""){
                    $theip = $ip;
    				if ( has_filter('ripm_show_ip') ) {
    					$theip = apply_filters('ripm_show_ip', $theip);
    				}
                    return $theip;
                } else {
                    $theip = '<em>'.__('None Recorded', 'register-ip-multisite').'</em>';
                    return $theip;
                }
            }
    	    return $value;
    	}
    

    You’ll notice the has_filter() check is only on one possible output? That’s because I’m translating the output on the other one, which says “None Recorded” I could filter that, so people could change it to anything they want, but right now I think that’s a bit odd.

    Filter The Output

    To test this, I made a file /mu-plugins/register-ip-multisite.php and put the following inside:

    function filter_ripm_show_ip($theip) {
        $theip = '<a href="https://duckduckgo.com/?q='.$theip.'" target="new">'.$theip.'</a>';
        return $theip;
    }
    add_filter('ripm_show_ip', 'filter_ripm_show_ip');
    

    That made it a link. Simple.

    Does It Work?

    Of course!

    Example of users screen with the IP showing as a link

    I did not apply the filter to the output on the edit-users page, but if that turns out to be needed, I can.

  • Apple Watch Faces

    Apple Watch Faces

    I’ve had my Apple Watch for getting near a year now.

    Time flies I guess.

    I started out using the basic Utility face, with the round clock and simple notifications along the edges. The more I used it, the more I realized I needed different faces for different days. That’s something the Watch lets me do with surprising ease.

    The Workday

    Modular face with schedule first

    What I need to know the most is ‘what do I have scheduled next?’ Meetings of course, but I also put appointments and I schedule ‘to do’ slots in there. I need a half day to fix some code? I’ll do block that out.

    The complications are the date in the upper left, the calendar in the center, and the bottom row are the weather, my activity, and MacID. The last one is a premium app which lets me lock and unlock my laptop from my watch. Since I leave my laptop open on my desk all the time, I find this helpful for security. Pop off the bathroom, lock the laptop. Come back, tap the watch, unlock.

    If you don’t have a watch, they have an iOS app that works too. In fact, the Watch uses the phone app.

    The Weekend

    Classic round watch with few complications

    I like an analog watch face. It’s my default. This one shows me the time and date with the day, the corners are battery and activity. The bottom is weather in detail. If I tap on the date, I go to my calendar, but most weekends I don’t need to worry about it.

    The Traveller

    Modular Face with weather first

    When I travel, I don’t actually need my calendar so much. It’s weird, but since I mostly go to WordCamps or similar conventions, I don’t tend to schedule things past ‘event – 8am to 5pm’ followed by ‘After Party – 6pm to 9pm’ and so on.

    That means what I need for my watch is the weather (I’m often in places for the first time). The bottom row is the time at home (so I don’t call my wife at one in the morning), activity, and my flight status. I almost always fly American these days, so I put that complication in.

    The Apps

    I mentioned MacID already. It’s $3.99 and worth it. I also use Wunderground for my weather, since it has the right kind of alerts. I can see the clouds and the rain and that’s all I want. I use the free version. American Airlines has a free app, and it’s surprisingly good about alerts for gate changes and rescheduled flights. I’m constantly getting updates before gate agents. I also use TripCase since I can track my hotel and event information in it. The bonus there is my wife can check and know where I’m supposed to be, in case she needs to get a hold of me.

    What Else

    I actually have other faces I use. I have a photo I took in Japan that I like to use with the time, and nothing more, when I have a non-stress, non-fuss day. I have a red-text time only face for when I’m in theaters (red light is less ‘glaring’ than others if I should happen to check the time). But the three I listed above are my regular watch faces. All on one watch.

  • On Saying No

    On Saying No

    We are, for the most part, accustomed to getting our way. We have a problem, we contact support, they fix it.

    Once in a while, however, support says ‘No.’

    I’m sorry, I can’t do that, Dave.

    There are technical limits to all products. Due to our own failures of imagination, we cannot foresee every possible iteration of usage for the things we build. These failures are, of course, not the fault of anything but our own lack of omniciense. We cannot know all things. We cannot predict all things.

    In this way, we have learned to expect limitations over time. We know that there is a line drawn in what the computer can do. If we have not programmed the computer to do a thing, it cannot know to do the thing. The reason for the limitation is just that the code’s author didn’t wish to write a thing. Why? Many reasons, none of which matter.

    We can, however, accept that systems and software are written by humans. Humans are fettered with limited vision. Computers are shackled by the humans who create them.

    When a program says it cannot do a thing, it cannot do the thing.

    I’m sorry, the system’s a bit limited.

    The problem with our shackles is that the one who has to tell a customer that the computer cannot do a thing is a person, not a computer.

    We cannot delete user accounts on WordPress.org for a number of reasons. If the site was just a blog, or a BuddyPress network, it would be a simple matter. Instead we integrated a wiki, multiple bbPress 1.x instances, a BuddyPress network, a multisite, theme SVN, plugin SVN, and core SVN.

    So no. We no longer have the technical ability to remove a user ID. Not even in the case where you accidentally used your gmail address as your username and are now ipstenugmailcom … It says username, but people do what they do. And no, we can’t rename users either. Same reason.

    We coded ourselves into a situation where we are technically limited. We cannot do the thing because we didn’t develop a way to do the thing.

    I’m sorry, but it’s against policy

    Then there’s a different kind of reason that someone tells you no. Like when someone asks for an embarrassing post or comment be deleted. Obviously this can be done. Comments and forum replies can always be deleted. That’s how they work. You may have your own site and you’ve deleted spam.

    But every site has a comment policy. They have the right to moderate their site however they want. Some delete things right away. Some moderate and manually approve all comments. Others let things run until the shit hits the fan and then spend hours and weeks and months cleaning up. However they choose to moderate and maintain their site is their business and their choice. You don’t have to stick around if you don’t like it.

    A large issue occurs when you don’t realize until after the fact that one of the policies is, perhaps, not removing posts. That’s when things get really messy, because now you’re being told no and not only is it by a human, but it’s a human who makes the choice.

    Your rights are subjective

    The rights you have on someone else’s website are subjective.

    The rights you have when you use a product are as well.

    If you download Microsoft Office, you agree to a lot of terms and conditions. Whether or not you read them, you agreed to them. Same with Apple’s iCloud terms and conditions.

    You give up some of your freedoms in exchange for their convenience. Your rights are subject to the agreements you make when you chose to use software, comment on a site, join a community, sign a contract, etc. etc.

    Be gracious in victory and defeat

    Once in a great while, someone will make an exception for you. Most of the time you really don’t want it.

    Perhaps you think it proves that all policies are mutable, but the reality is not. You see, that exception means it’s worth more to them to shut you up than it is to abide by policy.

    Of course a post can be deleted. Of course someone can lock your account for you. Of course you can be granted an extended warranty. But, for the most part, in order to get that far and get that level of ‘reward,’ you will have had to become the person no one wants to talk with.

    An example. A user asked for his account to be deleted and was informed of the technical impossibility of the request. He then asked for his posts to be removed and was informed of the policy prohibiting such a request. He finally asked for his account to be made inactive and to ban him from the site. He was told (after confirming that these requests were all to calm his paranoia and not that he was being harassed or stalked by someone) that the site was not his parents, and if he wanted to leave, the answer was to log off and walk away.

    Instead he began to post nothing but vulgarities.

    His account was locked and he was banned.

    He will likely never be welcome again.

    He might think he ‘won’ the argument, but all that happened was he showed his deplorable behavior, in public, in a way that Google captured. He tainted his reputation. He tarred and feathered himself. He burned his bridges. And he bragged about it.

    Support are people too

    When you are told ‘no,’ try to understand why. Accept the fact that you cannot get what you want all the time. Sometimes it’s just impossible. It’s understandable to be upset and angry. But the people tasks with enforcing policy or educating you to as to limitations, they are people just like you.

  • The Trouble With Libraries

    The Trouble With Libraries

    I’ve had the same argument over and over, to the point that people follow me here and complain that I suck. You’re welcome. I’m going to spell out the issues, as I personally see them with frameworks as plugins. If you want to talk about this in so far as it applies to the WordPress plugin repository, please read this post and keep the discussion there.

    The Problems

    Problem 1 – Users have no idea what the ‘library’ plugin is for.

    Most users understand “I have an add-on for WooCommerce, I probably need Woo.” They do not always understand “I have plugin Slider Joe. Why do I need Advanced Custom Fields?”

    Problem 2 – We don’t have true ‘dependancies’ in WordPress

    I think everyone can accept that’s a problem. We literally do not have a perfect way to require things. Look at Themes. If you try to delete a parent theme, you get warned there are children around. We don’t have that for plugins. We probably should.

    Problem 3 – Users are responsible for something they don’t understand.

    By having a library as a plugin, the onus of version compatibility and updates is now on the person least likely to understand it when it breaks: the user. They have to update the library, and your plugins, every time there’s a new version.

    Problem 4 – Frameworks and libraries can no longer break backwards compatibility.

    This is hugely restrictive, by the way. With a framework-as-plugin you can’t break things because you (the framework developer) are responsible for all the plugins that use your framework. If you break how things work from V1 to V2, and one of the myriad plugins a user has doesn’t work on V2, and the user updates your framework, you broke things. True, this was always the case, but at least the plugin contained the library and could use it’s own version.

    Problem 5 – Plugins will still break.

    I have to say ‘still’ because we have one version of the problem today, and we’ll have another tomorrow. Right now, if four plugins include the same library, and they’re all different versions, we don’t have a clear and perfect way to know which version of the library the user will get. Tomorrow, if a framework is a separate plugin, there’s absolutely no assurance than every plugin that requires that library has been tested with the version the user has install.

    The Options

    Today we really have two.

    Option 1 – Frameworks are plugins and users have to install them.

    This means all new plugins that include said framework have to remove it and change it to a require. All existing plugins should be contacted and told to change their code. Some users will complain about installing ‘extra’ plugins and some developers will lose users (it’s already happened).

    All developers have to put in this requirement themselves, possibly using a library like TGM (until we have dependancies). Also all developers have to ensure they, now and forever, keep up with the frameworks and ensure compatibility as well as proper alerts if a user removes the framework by accident. Their code has to break elegantly if the user doesn’t upgrade the library. Your plugin takes advantage of the latest feature in a framework? Awesome. Make sure your plugin checks “If this feature exists, use it” and fails gracefully if not.

    Option 2 – Frameworks that are not ‘functional’ frameworks, but really libraries are treated as all libraries are with all projects, and included separately.

    Developers have to code the calls to the library, making sure that the ‘right’ version is included no matter what someone else includes. Developers also have to update their plugins when a library updates. though if they properly handle the code calls, they don’t HAVE to. They could use namespaces and cleverly call use MYPLUGINAWSSDK as /aws/AWS/foo/bar instead, so their version is what’s used. They’ll probably want to code in a failsafe “If a version higher than mine is used, show a warning.”

    The Solution

    Looking at the options we have today, we have to ask “Which is better?”

    Neither. They both suck for developers. They both suck for the users. They both frustrate everyone. I have heard arguments from the same number of developers for each option. Some developers want to include the ‘core’ or a framework in their plugin because it’s ‘better’ than requiring another plugin. Other developers want the other plugin so they don’t have to be responsible to update the library.

    There is, clearly, an argument to be made in both cases. There isn’t a win here. Personally, I think once a framework or library exists as a plugin in the .org repository, you should remove it from your plugins and require it. Of course, good luck figuring out how to do that in a sane way without breaking people. The best I came up with was have a period of time where you keep the library while using TGM or something to require the other plugin. Make an alert or notice to tell users to install the requirement. Keep that for a whole major version. Then, on the next major version release, drop the library.

    With all that in mind, we have to ask this instead “Which option annoys users slightly less?”

    That’s #2 – libraries as libraries, not plugins. The one where the users don’t have to know (or care) about it anything except “I have plugin X.”

  • Unique vs Common

    Unique vs Common

    Someone asked me if I could explain why sometimes I tell people to name their functions uniquely and sometimes I don’t.

    I get how this is confusing, even though it makes perfect sense to me. Here’s how to remember:

    • What’s yours is unique to your plugin
    • What’s common is unique to what’s common

    Okay, maybe that didn’t help. Let’s try a practical example!

    I have a plugin called ‘Genericon’d’ which includes the Genericons library inside. In that plugin, I have the following enqueues:

            wp_enqueue_style( &#039;genericons&#039;, plugins_url( &#039;genericons/genericons/genericons.css&#039;, __FILE__ , &#039;&#039;, self::$gen_ver ) );
            wp_enqueue_style( &#039;genericond&#039;, plugins_url( &#039;css/genericond.css&#039;, __FILE__ , &#039;&#039;, self::$gen_ver ) );
    

    The first one is for Genericons, the library. There I use the common name of genericons which means if anyone else also enqueues Genericons with this name, one of ours will win depending on load order. For my personal plugin CSS, I use the name genericond (notice that D?) as it matches the name of my plugin and, therefore, is less likely to conflict with someone else’s plugin.

    Conflict is the name of the game here. I actually want genericons to conflict. Well, not conflict. I want it to be recognized as a shared library, so WordPress only loads it once. If I had named it genericond-genericons then, in the event someone has Jetpack or a theme like TwentyFifteen which both include Genericons, they would load the entire library twice! And I would suck. On the other hand, if we all share the library name, WordPress will check, see it’s loaded, and carry on.

    Name what’s yours in a way unique to your plugin or theme.

    Name what’s shared in a way that’s unique, but also stupid obvious and easy to share.

    Font-Awesome? Use font-awesome for your enqueues.

    As for functions and class names, even if you put in a check for ‘if this doesn’t exist…’ the logic works the same. If it’s yours and yours alone and only exists in this code, name it unique to your plugin/theme. If it’s a shared library, like the aws-sdk, name the class in the standard way and wrap it in an ‘if this doesn’t already exist…’ so that if it does exist, it only gets called once.

    Standard names for shared libraries.

    Unique names for your personal codes.

    And bonus! If you keep your names to a standard, while being unique, someone may call your functions too.

    By the way, anything global like DEFINE( 'FOO', true ); should always be unique. Those are globals. Everyone uses them. I’ve made that mistake too.