Performance as Design

It’s time for us to treat performance as an essential design feature, not just as a technical best practice.

74% of mobile web users [9] will leave a site if it takes longer than 5 seconds to load.

Good performance is good design

The road towards better performance doesn’t start with developers or technology stacks (though I’m certainly not suggesting those things are unimportant). It begins with a shared interest on everyone’s part in making a product that’s lightning fast.

Ultimately performance is about respect. Respect your users’ time and they will be more likely to walk away with a positive experience. Good performance is good design. It’s time we treat it as such.

Original article

Apr 14, 2014

Groovy like map constructor

I miss so much the Groovy like map constructor in Java that I have created my own.

public class MyBean{
  public MyBean(Map<String, ?> map){
    JakartaPropertyUtils.copyProperties(this, map);
  }
}

The copyProperties helper is a simple wrapper around Jakarta commons-beanutils implementation to eliminate useless checked exception handling.

Why is it useful:

Apr 13, 2014

Vezess úgy, hogy ne legyen rád szükség! - Tudatos Vezetés

Abban bíznak, hogy a helyi kollégák ismerik és értik a legjobban a piaci viszonyokat. Neki az a feladata, hogy széles körű tapasztalatai alapján feltett kérdéseivel segítsen a vezetőknek a pontos helyzetértékelésben és megoldáskeresésben. Nem akarja megmondani a választ mindenre, hiszen nincs is válasza az összes problémára. Nagyon szívesen segít, ha arra kérik, nem csak kérdésekkel, hanem tudással, erőforrással és kapcsolatokkal is. Ha nincs jobb ötletük, mint amit helyben javasolnak, akkor a kockázatok ellenére engedik a próbálkozást. Lehet hibázni, persze nem folyamatosan és nem ugyanabban. Tudják és elfogadják, hogy a vállalkozás nem arról szól, hogy minden bejön.

lehetetlen úgy vezetni, hogy mindent kontrolláljon az ember. Helyette olyan rendszereket kell kialakítani, hogy a kollégák rálássanak egymás tevékenységére és időben érzékeljék, ha valami nem megfelelően megy. … kölcsönös bizalmi viszonyt kell kialakítani.

minden kollégának vannak erősségei és korlátai. Alapvetően az erősségere kell építeni,

Eredeti

Apr 07, 2014

Euroszkepticizmus

az euroszkepticizmus igazi alapjait a munkakultúrában kell keresni. Minden más csupán duma.

Északnyugat-Európában kialakult egy sajátos életszemlélet, amelyet egy viking szólás igen jól szimbolizál: „Erőmben bízom, semmi másban!”. …Aztán, a XIX. század folyamán, amikor létrejött a német egység, Németország (szintén bármiféle „amerikai program” nélkül) Európa legdinamikusabb államává vált, éspedig néhány évtized alatt. És ezek azok az évtizedek, amikor igazán „befutottak” a skandináv országok is – elsőként Svédország. Mert a kifejlődött ipari társadalom és a „munkacentrikus” északi mentalitás igen eredményes „elegyet” alkottak… Igen: könnyebb leszólni Németországot, könnyebb azt állítani, hogy mindaz, amit elért, „nem is saját magának köszönhető”, mintsem tanulni tőle kik manapság (egyre hangosabban és magabiztosabban, egyre nagyobb tömegben és egyre nyíltabban) az EU további egységesülése ellen beszélnek, valójában meg szeretnék úszni azt, hogy teljesítmény tekintetében fel kelljen zárkózniuk az északi/centrumországokhoz nem kötelező mindenkinek „németté/skandinávvá válni” – de ennek ára lesz. Amikor kialakult a „mai” brit mentalitás és közösségtudat, az volt a teljesítményorientáltság célja, hogy meggazdagodván többé ne kelljen „megfeszülni”, hanem a pozíció önmagában, „arisztokratikusan” garantálja majd a jólétet. ellentmondás: mindent megtenni azért, hogy minden, ami elérhető, a miénk legyen – aztán elvárni, hogy ez így is maradjon, örökkön-örökké, még akkor is, amikor mások, akik nem pihennek a babérokon, technológiában, tudományban, szervezésben, erőben, ebben-abban immár elibénk kerültek Hiába lenne hosszú távon a britek érdeke is az egységes, bivalyerős és magasan teljesítő EU, ha ma, amikor dönteni kell, sok brit inkább választja a ma (még) csábítóbbat, a jól megszokott „splendid isolation” …. az euroszkepticizmus igazi alapjait a munkakultúrában kell keresni. Minden más csupán duma.

http://progressziv.blog.hu/2012/06/18/ki_nyeri_a_ii_vilaghaborut

Apr 06, 2014

Six Tips for Editing Your Resume - Rothman

  1. Write a first draft of your resume. Write it in reverse chronological order, with your most recent job first. Now, put it away for a few days, while you do other things. Your subconscious will work on it while you work on the rest of your job search. Your subconscious is working on the details.
  2. Now it’s time to do the hard work of the details for your resume. Especially for the most recent work on your resume, fill in the details. Assume you will write in prose here. Use good grammar. Don’t worry about the length of your resume yet. You can trim later. For each line of your resume, explain what you did, how you did it, and how you added value to the organization. Use numbers about time saved on projects, cost saved on projects, customers added or retained, revenue added, that kind of thing. If you can’t describe how you added value, does that line belong on your resume?
  3. If you have keywords, put those on the bottom of the resume, after all your experience. Do not clutter the most valuable part of your resume, the top, with keywords. Yes, I know the ATS wants to see keywords. The ATS is a robot. The ATS doesn’t care where the keywords are. The human reading your resume does care.
  4. Check for typos. This is where you can run your resume through Grammarly’s online grammar check and see what Grammarly says. Does Grammarly say, “Schlub. Not readable. Inconsistent grammar.” Or, does Grammarly say, “Professional. Your previous managers, your colleagues, even your mom would be proud.”
  5. Ask other people to review your resume. Does it make sense? Would they want to hire you, based on this resume? Have you boxed yourself into a job that doesn’t exist, or a domain that is shrinking? This is the time to ask.
  6. Now, see how long your resume is. If it’s longer than two pages, what do you need to trim?

Original article

Apr 01, 2014

Archive comparison

Épp a minap futottam bele egy igen nehéz JBoss és classloader problémába. Persze mi voltunk a hibásak, de azért elvitte az egész napot, amíg megtaláltuk a probléma forrását.

A gond az volt, hogy egy EAR filon belül azegyik WAR tartalmazott egy extra és felesleges log4j jar-t, ami a classloader sajátosságai miatt nem talált configurációt (noha a JBoss jól volt globálisan beállítva) és emiatt az alapértelmezett beállításokat használta, ami minden debug szinten tolt ki. És beleérte valami thirdparty komponest is, ami teljesen kiakasztotta a rendszert. A megoldás az lett, hogy ear shared library szintre hoztuk a log4j jar-t (pontosabban már ott volt, csak a war-ból kellett kegyomlálni). A végén csak egy kis pom file bűvészkedés volt a megoldás.

És hogyan kerüljük el a hasonló problémákat a jövőben? Az a stratégia, hogy a fejlesztés végén a “before” és “after” ear fájlokat üsszehasonlítjuk és a változások alapján rábólintunk, vagy megnézzük még egy alkalommal.

A munka dandárját egy olyan kis utility végzi, ami hierarchikusan kicsomagolja az egymásba ágyazott zip állományokat.

import groovy.io.FileType

import java.security.MessageDigest

File.metaClass.extension {
  path.substring(path.lastIndexOf(".") + 1)
}
File.metaClass.isZip {
  ["zip", "ear", "war"].contains(extension())
}

def LeftFile = /c:\environment\_work\ZipComparison\x\LeftFile.zip/
def RightFile = /c:\environment\_work\ZipComparison\x\RightFile.zip/

Map leftHashes = generateHash(cleanup(unzip(new File(LeftFile))))
Map rightHashes = generateHash(cleanup(unzip(new File(RightFile))))
Map diff = mapDiff(leftHashes, rightHashes)
printReport(diff)


def unzip(File path) {
  if (!path.isZip()) {
    return path
  }
  File destination = new File(path.path + ".dir")
  if (destination.exists())
    destination.deleteDir()
  destination.mkdirs()
  def ant = new AntBuilder()
  ant.project.getBuildListeners().firstElement().setMessageOutputLevel(1)
  ant.unzip(src: path.path,
      dest: destination.path,
      overwrite: "false")

  destination.eachFileRecurse(FileType.FILES) { File child ->
    unzip(child)
  }
  return destination
}

def cleanup(File file) {
  file.eachFileRecurse(FileType.FILES) { File f ->
    if (f.isZip()) {
      f.delete()
    }
  }
  return file;
}

def generateHash(File dir) {
  Map hashes = [:]
  dir.eachFileRecurse(FileType.FILES) { File file ->
    def md = generateMD5(file)
    hashes[file.path.substring(dir.path.length() + 1)] = md
  }
  hashes
}

def generateMD5(final File file) {
  MessageDigest digest = MessageDigest.getInstance("MD5")
  file.withInputStream() { is ->
    byte[] buffer = new byte[8192]
    int read = 0
    while ((read = is.read(buffer)) > 0) {
      digest.update(buffer, 0, read);
    }
  }
  byte[] md5sum = digest.digest()
  BigInteger bigInt = new BigInteger(1, md5sum)
  return bigInt.toString(16).padLeft(32, '0')
}


def mapDiff(Map oldMap, Map newMap) {
  def newKeys = newMap*.key
  def oldKeys = oldMap*.key

  def removedKeys = oldKeys - newKeys
  def addedKeys = newKeys - oldKeys
  def commonKeys = newKeys - removedKeys - addedKeys
  def changedKeys = commonKeys.findAll { oldMap[it] != newMap[it] }
  def unchangedKeys = commonKeys - changedKeys

  def changes = [
      removed: oldMap.findAll { it.key in removedKeys },
      added: newMap.findAll { it.key in addedKeys },
      changed: oldMap.findAll { it.key in changedKeys },
      unchanged: newMap.findAll { it.key in unchangedKeys }
  ]
}

private void printReport(Map diff) {
  println "Only In Left"
  println "============"
  diff["removed"].each {
    println it.key
  }
  println "Only In Right"
  println "============"
  diff["added"].each {
    println it.key
  }
  println "Changed"
  println "============"
  diff["changed"].each {
    println it.key
  }
}
Mar 24, 2014

Sure sign whether a bug or feature

The only way to decide whether a change is a bugfix or new feature: If it is a bug all test pass. If it is a feature some (other) tests fail.

Mar 05, 2014

Nem a luxusra vágyunk hanem minőségre

A minőséggel és annak következményeivel első alkalommal A zen meg a motorkerékpár-ápolás művészete könyv kapcsán kerültem közel. Akkor még csak körvanalazódott, de most már határozottan tudom, hogy az emberek nem a luxust keresik az életben, hanem a minőséget.

De az ért nem ilyen egyszerűen jöttem rá. Második igazi löket ebben a témában a Timothy Ferriss: 4 órás munkahét. Nem kevesebbet állít, mint, hogy nem a gazdagságra vágynak az emberek, hanem azon dolgokra, amit szerintük sok pénzzel lehet elérni. nagyon jó példákat hozot fel arra, hogy miért nem igaz. Sokan szeretnek utazni. Sokunkat visszafognak a magas szállodai árak. Azért nem maradunk sokáig. De gondolj bele valószínűleg alig pár napis max egy hetes szállodai költség felyében egy egész hünapra ki tudsz venni egy egész lakást. a kérdés az, hogy a szálodai szolgáltatások miatt mész utazni vagy azért, hogy megismerd a világot?

És magamben összeraktam a kettőt. A pénz és gazdagság helyett a minőségre vágyunk. A mindennapjainkat konfortját nem az határozza meg, hogy mi menniybe kerül, hanem az, hogy mennyire nem akadájozzák az életünket.

A teljes képhez hozzátartozik, hogy a minőséghez álltalában több pénz is kell. De én azt tapasztaltam, hogy jóval kevesebb, mint gondolnánk.

Nehezebb elmondani, mint példákkal illusztrálni:

Soroljam még…

Feb 19, 2014

Ígéret

Ígérni lehet bármit.

Az ígéret egy érdekes jószág. De fura, ha automatikusan hiszünk az ígéreteknek. Csak a következő esetek lehetnek racionálisak ígéretekkel kapcsolatban:

  1. Illető múltbeli eredményeit vizsgálod és azt kivetíted saját véleményed szerint és az alapján ítéled meg, hogy arra visz-e ami neked tetszik. Itt tulajdonképpen figyelmen kívül hagyod az ígéretet csak az embert nézed.

  2. Ismered az illető múltját és az alapján azt, hogy az ígéreteit a múltban milyen hatékonysággal teljesítette.

De mint látszik. ismeretlen embernek nem lehet hinni. Olyannak, aki már korábban is szegett meg ígéretet, annak sem.

Minden más ígéretre hallgatás súlyos önbecsapás

NB: Eredeti vázlat - 2011.08.29.

Feb 16, 2014

Smell of bad design

When you read such a mail after a difficult investigation of a bug/code review…

”… at line 16578 I have found …”

… and you realize that it is in the middle of that component only.

Feb 05, 2014

Jakarta Equivalence Relation

The Need

Given a big and complex (objects in objects, maps, arrays, lists, child object etc) object structure.

And I would like to check weather two set of objects are equals or not.

I could not use equals() because it is ialready implemented but it not complete (not all attribute included) and I could not decide whether it is a featrure or bug. Plus the input is two arrays of object and as we all know Java array implementation gives a shit to equals.

Secondary need: once a difference is found I would like to know where it is failed on the object hierarchy exactly. Just think about a little bit. If something is not equal you still need to know why.

Let’s start in small

public interface EquivalenceRelation {
  public final static EquivalenceRelation DEFAULT = new EquivalenceRelation() {
    public boolean areEquals(Object o1, Object o2) {
      return o1 == o2 || o1 != null && o1.equals(o2);
    }
  };

  public boolean areEquals(Object o1, Object o2);
}

So far so good. But we should not forget about “reporting” differences.

public abstract class ReportingEquivalenceRelation implements EquivalenceRelation {
  final List<String> messages = new ArrayList<String>();
  int indent = -1;
  static String END_MARKER = "<!--diff -->";
  static String INDENT = "   ";

  public ReportingEquivalenceRelation() {
  }

  public ReportingEquivalenceRelation(String title) {
    addMessage(title);
  }

  public boolean areEquals(Object o1, Object o2) {
    indent();
    boolean res = _areEquals(o1, o2);
    unindent();
    if (!res && !alreadyEndMarked()) {
      addMessage(END_MARKER);
    }
    return res;
  }

  private boolean alreadyEndMarked() {
    return messages.size() > 0 && messages.get(messages.size() - 1).endsWith(END_MARKER);
  }

  public abstract boolean _areEquals(Object o1, Object o2);

  public List<String> getMessages() {
    return messages;
  }

  public String getMessage() {
    return Joiner.on("\n").join(messages);
  }

  public void addMessage(String s) {
    messages.add(indentation() + s);
  }

  private String indentation() {
    String res = "";
    for (int i = 0; i < indent; i++) {
      res += INDENT;
    }
    return res;
  }

  public void indent() {
    indent++;
  }

  public void unindent() {
    indent--;
  }
}

How to implement in a generic way? Trivial answer: use some kind of reflection. Working directly with reflection in java is pain. But luckily there are alternatives, wrappers like Jakarta Beanutils.

(NB: Even if some of the classes used are not familiar you could check in github - see later - or trivial enough - like FIterable)

public class JakartaEquivalenceRelation extends ReportingEquivalenceRelation {
  private Set<Pair<Integer, Integer>> registry = new HashSet<Pair<Integer, Integer>>();

  public JakartaEquivalenceRelation() {
  }

  public JakartaEquivalenceRelation(String title) {
    super(title);
  }

  @Override
  public boolean _areEquals(Object o1, Object o2) {
    if (o1 == o2) {
      return true;
    }
    if (null == o1 || null == o2) {
      return false;
    }
    if (isPrimitive(o1) || isPrimitive(o2)) {
      return equalsPrimitive(o1, o2);
    }
    return beanEquals(o1, o2);
  }

  static Predicate<PropertyDescriptor> class_attribute = AppPredicates.propEq("name", "class");
  static Predicate<PropertyDescriptor> no_class_attribute = Predicates.not(class_attribute);
  static Function<PropertyDescriptor, String> byName = AppFunction.byProp("name");

  private boolean beanEquals(Object o1, Object o2) {
    register(o1, o2);
    if (isArray(o1)) {
      return equalsArrays(o1, o2);
    }
    if (isList(o1)) {
      return equalsList(o1, o2);
    }
    if (isSet(o1)) {
      throw new RuntimeException("Set equvalence isnot supported");
    }
    if (isMap(o1)) {
      return equalsMap(o1, o2);
    }
    //plain object
    Map<String, PropertyDescriptor> pd1 = 
		FIterable.from(getPropertyDescriptors(o1)).filter(no_class_attribute).asMap(byName);
    Map<String, PropertyDescriptor> pd2 = 
		FIterable.from(getPropertyDescriptors(o2)).filter(no_class_attribute).asMap(byName);
    if (pd1.size() != pd2.size()) {
      addMessage("size diff");
      return false;
    }
    if (!(pd1.keySet().equals(pd2.keySet()))) {
      addMessage("nr of bean properties diff");
      return false;
    }
    for (String prop : pd1.keySet()) {
      Object p1 = JakartaPropertyUtils.getProperty(o1, prop);
      Object p2 = JakartaPropertyUtils.getProperty(o2, prop);
      addMessage(rs("[{}:`{}` vs `{}`]", prop, simplify(p1), simplify(p2)));
      if (isRegistered(p1, p2)) {
        continue;
      }
      if (!areEquals(p1, p2)) {
        return false;
      }
    }
    return true;
  }

  private boolean isMap(Object o1) {
    return o1 instanceof Map;
  }

  private boolean isSet(Object o1) {
    return o1 instanceof Set;
  }

  private boolean isList(Object o1) {
    return o1 instanceof List;
  }

  private Object simplify(Object p1) {
    if (isPrimitive(p1)) {
      return p1;
    }
    return "<complex>";
  }

  private boolean isRegistered(Object o1, Object o2) {
    Pair<Integer, Integer> pos = Pair.of(System.identityHashCode(o1), System.identityHashCode(o2));
    return registry.contains(pos);
  }

  private void register(Object o1, Object o2) {
    Pair<Integer, Integer> pos = Pair.of(System.identityHashCode(o1), System.identityHashCode(o2));
    registry.add(pos);
  }

  private boolean equalsPrimitive(Object o1, Object o2) {
    return o1.equals(o2);
  }

  private boolean isPrimitive(Object o1) {
    return o1 == null
        || o1.getClass().isPrimitive()
        || o1 instanceof Boolean
        || java.lang.Number.class.isAssignableFrom(o1.getClass())
        || java.lang.String.class.isAssignableFrom(o1.getClass())
        || java.lang.Object.class.equals(o1.getClass())
        || o1 instanceof Date;
  }

  private boolean equalsMap(Object o1, Object o2) {
    addMessage("MAP");
    Map<?, ?> a = (Map<?, ?>) o1;
    Map<?, ?> b = (Map<?, ?>) o2;
    if (a.size() != b.size()) {
      addMessage("size diff");
      return false;
    }
    for (Object k : a.keySet()) {
      Object va = a.get(k);
      Object vb = b.get(k);
      addMessage(rs("[{}:`{}` vs `{}`]", k, va, vb));
      boolean r = areEquals(va, vb);
      if (!r) {
        return false;
      }
    }
    return true;
  }

  private boolean equalsList(Object o1, Object o2) {
    addMessage("LIST");
    List<?> l1 = (List<?>) o1;
    List<?> l2 = (List<?>) o2;
    if (l1.size() != l2.size()) {
      addMessage("size diff");
      return false;
    }
    for (int i = 0; i < l1.size(); i++) {
      addMessage(i + ".");
      boolean r = areEquals(l1.get(i), l2.get(i));
      if (!r) {
        return false;
      }
    }
    return true;
  }

  private boolean isArray(Object o1) {
    return o1.getClass().isArray();
  }

  private boolean equalsArrays(Object o1, Object o2) {
    addMessage("ARRAY");
    Object[] oa1 = (Object[]) o1;
    Object[] oa2 = (Object[]) o2;
    if (oa1.length != oa2.length) {
      addMessage("Length diff");
      return false;
    }
    for (int i = 0; i < oa1.length; i++) {
      addMessage(i + ".");
      boolean r = areEquals(oa1[i], oa2[i]);
      if (!r) {
        return false;
      }
    }
    return true;
  }
}

Download full source code with test from github

Jan 29, 2014

Archives

Links

Cool

RSS